| /* |
| * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // Contributors: |
| // dclarke, mnorman - Dynamic Persistence |
| // http://wiki.eclipse.org/EclipseLink/Development/Dynamic |
| // (https://bugs.eclipse.org/bugs/show_bug.cgi?id=200045) |
| // 14/05/2012-2.4 Guy Pelletier |
| // - 376603: Provide for table per tenant support for multitenant applications |
| package org.eclipse.persistence.dynamic; |
| |
| //javase imports |
| import java.lang.reflect.Field; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| |
| //EclipseLink imports |
| import org.eclipse.persistence.descriptors.ClassDescriptor; |
| import org.eclipse.persistence.expressions.ExpressionBuilder; |
| import org.eclipse.persistence.internal.dynamic.DynamicEntityImpl; |
| import org.eclipse.persistence.internal.dynamic.DynamicPropertiesManager; |
| import org.eclipse.persistence.internal.dynamic.DynamicTypeImpl; |
| import org.eclipse.persistence.internal.sessions.AbstractSession; |
| import org.eclipse.persistence.queries.ReadAllQuery; |
| import org.eclipse.persistence.queries.ReadObjectQuery; |
| import org.eclipse.persistence.queries.ReportQuery; |
| import org.eclipse.persistence.sessions.DatabaseSession; |
| import org.eclipse.persistence.sessions.Session; |
| import org.eclipse.persistence.tools.schemaframework.DynamicSchemaManager; |
| |
| /** |
| * A DynamicHelper provides some utility methods to simplify application |
| * development with dynamic types. Since the application does not have static |
| * references to the dynamic types it must use entity names. This helper |
| * provides simplified access to methods that would typically require the static |
| * classes. |
| * |
| * @author dclarke, mnorman |
| * @since EclipseLink 1.2 |
| */ |
| public class DynamicHelper { |
| |
| protected DatabaseSession session; |
| protected Map<String, ClassDescriptor> fqClassnameToDescriptor = |
| new HashMap<>(); |
| |
| public DynamicHelper(DatabaseSession session) { |
| this.session = session; |
| Collection<ClassDescriptor> descriptors = session.getDescriptors().values(); |
| for (ClassDescriptor desc : descriptors) { |
| if (desc.getJavaClassName() != null) { |
| fqClassnameToDescriptor.put(desc.getJavaClassName(), desc); |
| } |
| } |
| } |
| |
| public DatabaseSession getSession() { |
| return this.session; |
| } |
| |
| /** |
| * Lookup the dynamic type for an alias. This is required to get the type |
| * for factory creation but can also be used to provide the application with |
| * access to the meta model (type and properties) allowing for dynamic use |
| * as well as optimized data value retrieval from an entity. |
| */ |
| public DynamicType getType(String typeName) { |
| ClassDescriptor cd = fqClassnameToDescriptor.get(typeName); |
| if (cd == null) { |
| cd = getSession().getClassDescriptorForAlias(typeName); |
| } |
| |
| if (cd == null) { |
| return null; |
| } |
| return getType(cd); |
| } |
| |
| public static DynamicType getType(ClassDescriptor descriptor) { |
| return (DynamicType) descriptor.getProperty(DynamicType.DESCRIPTOR_PROPERTY); |
| } |
| |
| /** |
| * Provide access to the entity's type. |
| * |
| * @throws ClassCastException |
| * if entity is not an instance of {@link DynamicEntityImpl} |
| */ |
| public static DynamicType getType(DynamicEntity entity) throws ClassCastException { |
| return ((DynamicEntityImpl) entity).getType(); |
| } |
| |
| /** |
| * Remove a dynamic type from the system. |
| * |
| * This implementation assumes that the dynamic type has no relationships to |
| * it and that it is not involved in an inheritance relationship. If there |
| * are concurrent processes using this type when it is removed some |
| * exceptions may occur. |
| * |
| */ |
| public void removeType(String typeName) { |
| DynamicType type = getType(typeName); |
| |
| if (type != null) { |
| getSession().getIdentityMapAccessor().initializeIdentityMap(type.getJavaClass()); |
| |
| ClassDescriptor descriptor = type.getDescriptor(); |
| fqClassnameToDescriptor.remove(descriptor.getJavaClassName()); |
| getSession().getProject().getOrderedDescriptors().remove(descriptor); |
| getSession().getProject().getDescriptors().remove(type.getJavaClass()); |
| //bug 430318 - clear the parsed cache as queries in that cache could be using this descriptor |
| getSession().getProject().getJPQLParseCache().clear(); |
| ((AbstractSession)getSession()).getCommitManager().getCommitOrder().remove(type.getJavaClass()); |
| } |
| } |
| |
| /** |
| * |
| */ |
| public DynamicEntity newDynamicEntity(String typeName) { |
| DynamicType type = getType(typeName); |
| |
| if (type == null) { |
| throw new IllegalArgumentException("DynamicHelper.createQuery: Dynamic type not found: " + typeName); |
| } |
| |
| return type.newDynamicEntity(); |
| } |
| |
| /** |
| * Helper method to simplify creating a native ReadAllQuery using the entity |
| * type name (descriptor alias) |
| */ |
| public ReadAllQuery newReadAllQuery(String typeName) { |
| DynamicType type = getType(typeName); |
| |
| if (type == null) { |
| throw new IllegalArgumentException("DynamicHelper.createQuery: Dynamic type not found: " + typeName); |
| } |
| |
| return new ReadAllQuery(type.getJavaClass()); |
| } |
| |
| /** |
| * Helper method to simplify creating a native ReadObjectQuery using the |
| * entity type name (descriptor alias) |
| */ |
| public ReadObjectQuery newReadObjectQuery(String typeName) { |
| DynamicType type = getType(typeName); |
| |
| if (type == null) { |
| throw new IllegalArgumentException("DynamicHelper.createQuery: Dynamic type not found: " + typeName); |
| } |
| |
| return new ReadObjectQuery(type.getJavaClass()); |
| } |
| |
| /** |
| * Helper method to simplify creating a native ReportQuery using the entity |
| * type name (descriptor alias) |
| */ |
| public ReportQuery newReportQuery(String typeName, ExpressionBuilder builder) { |
| DynamicType type = getType(typeName); |
| |
| if (type == null) { |
| throw new IllegalArgumentException("DynamicHelper.createQuery: Dynamic type not found: " + typeName); |
| } |
| |
| return new ReportQuery(type.getJavaClass(), builder); |
| } |
| |
| public DynamicClassLoader getDynamicClassLoader() { |
| return DynamicClassLoader.lookup(getSession()); |
| } |
| |
| /** |
| * Add one or more EntityType instances to a session and optionally generate |
| * needed tables with or without FK constraints. |
| * |
| */ |
| public void addTypes(boolean createMissingTables, boolean generateFKConstraints, DynamicType... types) { |
| if (types == null || types.length == 0) { |
| throw new IllegalArgumentException("No types provided"); |
| } |
| Collection<ClassDescriptor> descriptors = new ArrayList<>(types.length); |
| for (int index = 0; index < types.length; index++) { |
| DynamicType typ = types[index]; |
| ClassDescriptor desc = typ.getDescriptor(); |
| DynamicTypeImpl typImpl = ((DynamicTypeImpl)typ); |
| typImpl.setDescriptor(desc); |
| try { |
| Field dpmField = desc.getJavaClass().getField(DynamicPropertiesManager.PROPERTIES_MANAGER_FIELD); |
| DynamicPropertiesManager dpm = (DynamicPropertiesManager)dpmField.get(null); |
| dpm.setType(typImpl); |
| typImpl.setDynamicPropertiesManager(dpm); |
| } |
| catch (Exception e) { |
| // this is bad! Without the dpmField, not much to do but die :-( ... |
| e.printStackTrace(); |
| return; |
| } |
| descriptors.add(desc); |
| if (!types[index].getDescriptor().requiresInitialization((AbstractSession) session)) { |
| types[index].getDescriptor().getInstantiationPolicy().initialize((AbstractSession) session); |
| } |
| } |
| session.addDescriptors(descriptors); |
| for (ClassDescriptor desc : descriptors) { |
| if (desc.getJavaClassName() != null) { |
| fqClassnameToDescriptor.put(desc.getJavaClassName(), desc); |
| } |
| } |
| |
| if (createMissingTables) { |
| if (!getSession().isConnected()) { |
| getSession().login(); |
| } |
| new DynamicSchemaManager(session).createTables(generateFKConstraints, types); |
| } |
| } |
| |
| /** |
| * A SessionCustomizer which configures all descriptors as dynamic entity |
| * types. |
| */ |
| public static class SessionCustomizer implements org.eclipse.persistence.config.SessionCustomizer { |
| |
| /** |
| * Default constructor. |
| */ |
| public SessionCustomizer() { |
| } |
| |
| @Override |
| public void customize(Session session) throws Exception { |
| DynamicClassLoader dcl = DynamicClassLoader.lookup(session); |
| |
| for (Iterator<?> i = session.getProject().getDescriptors().values().iterator(); i.hasNext();) { |
| new DynamicTypeBuilder(dcl, (ClassDescriptor) i.next(), null); |
| } |
| } |
| } |
| |
| } |