blob: 7174d376105465cc103e1ddee8aa2bbfef10b23e [file] [log] [blame]
/*
* 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 {
@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);
}
}
}
}