blob: 977b82479060d7141587b3b3d234a3defb0f4123 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
<<<<<<< Updated upstream
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
=======
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
>>>>>>> Stashed changes
* limitations under the License.
*/
/*
* JDOHelper.java
*
*/
package javax.jdo;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.jdo.spi.I18NHelper;
import javax.jdo.spi.JDOImplHelper;
import javax.jdo.spi.JDOImplHelper.StateInterrogationBooleanReturn;
import javax.jdo.spi.JDOImplHelper.StateInterrogationObjectReturn;
import javax.jdo.spi.PersistenceCapable;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.rmi.PortableRemoteObject;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
/**
* This class can be used by a JDO-aware application to call the JDO behavior of <code>
* PersistenceCapable</code> instances without declaring them to be <code>PersistenceCapable</code>.
*
* <p>It is also used to acquire a <code>PersistenceManagerFactory</code> via various methods.
*
* <p>This helper class defines static methods that allow a JDO-aware application to examine the
* runtime state of instances. For example, an application can discover whether the instance is
* persistent, transactional, dirty, new, deleted, or detached; and to get its associated <code>
* PersistenceManager</code> if it has one.
*
* @version 2.1
*/
public class JDOHelper implements Constants {
/** A mapping from jdoconfig.xsd element attributes to PMF properties. */
static final Map<String, String> ATTRIBUTE_PROPERTY_XREF = createAttributePropertyXref();
/** The Internationalization message helper. */
private static final I18NHelper MSG = I18NHelper.getInstance("javax.jdo.Bundle"); // NOI18N
/**
* Creates a map from jdoconfig.xsd element attributes to PMF properties.
*
* @return An unmodifiable Map of jdoconfig.xsd element attributes to PMF properties.
*/
static Map<String, String> createAttributePropertyXref() {
Map<String, String> xref = new HashMap<>();
xref.put(PMF_ATTRIBUTE_CLASS, PROPERTY_PERSISTENCE_MANAGER_FACTORY_CLASS);
xref.put(PMF_ATTRIBUTE_CONNECTION_DRIVER_NAME, PROPERTY_CONNECTION_DRIVER_NAME);
xref.put(PMF_ATTRIBUTE_CONNECTION_FACTORY_NAME, PROPERTY_CONNECTION_FACTORY_NAME);
xref.put(PMF_ATTRIBUTE_CONNECTION_FACTORY2_NAME, PROPERTY_CONNECTION_FACTORY2_NAME);
xref.put(PMF_ATTRIBUTE_CONNECTION_PASSWORD, PROPERTY_CONNECTION_PASSWORD);
xref.put(PMF_ATTRIBUTE_CONNECTION_URL, PROPERTY_CONNECTION_URL);
xref.put(PMF_ATTRIBUTE_CONNECTION_USER_NAME, PROPERTY_CONNECTION_USER_NAME);
xref.put(PMF_ATTRIBUTE_IGNORE_CACHE, PROPERTY_IGNORE_CACHE);
xref.put(PMF_ATTRIBUTE_MAPPING, PROPERTY_MAPPING);
xref.put(PMF_ATTRIBUTE_MULTITHREADED, PROPERTY_MULTITHREADED);
xref.put(PMF_ATTRIBUTE_NONTRANSACTIONAL_READ, PROPERTY_NONTRANSACTIONAL_READ);
xref.put(PMF_ATTRIBUTE_NONTRANSACTIONAL_WRITE, PROPERTY_NONTRANSACTIONAL_WRITE);
xref.put(PMF_ATTRIBUTE_OPTIMISTIC, PROPERTY_OPTIMISTIC);
xref.put(PMF_ATTRIBUTE_PERSISTENCE_UNIT_NAME, PROPERTY_PERSISTENCE_UNIT_NAME);
xref.put(PMF_ATTRIBUTE_NAME, PROPERTY_NAME);
xref.put(PMF_ATTRIBUTE_RESTORE_VALUES, PROPERTY_RESTORE_VALUES);
xref.put(PMF_ATTRIBUTE_RETAIN_VALUES, PROPERTY_RETAIN_VALUES);
xref.put(PMF_ATTRIBUTE_DETACH_ALL_ON_COMMIT, PROPERTY_DETACH_ALL_ON_COMMIT);
xref.put(PMF_ATTRIBUTE_SERVER_TIME_ZONE_ID, PROPERTY_SERVER_TIME_ZONE_ID);
xref.put(PMF_ATTRIBUTE_DATASTORE_READ_TIMEOUT_MILLIS, PROPERTY_DATASTORE_READ_TIMEOUT_MILLIS);
xref.put(PMF_ATTRIBUTE_DATASTORE_WRITE_TIMEOUT_MILLIS, PROPERTY_DATASTORE_WRITE_TIMEOUT_MILLIS);
return Collections.unmodifiableMap(xref);
}
/** The JDOImplHelper instance used for handling non-binary-compatible implementations. */
private static final JDOImplHelper IMPL_HELPER =
doPrivileged((PrivilegedAction<JDOImplHelper>) JDOImplHelper::getInstance);
/**
* The singleton instance of JDOHelper.
*
* @since 2.1
*/
private static final JDOHelper INSTANCE = new JDOHelper();
/**
* Return the singleton instance of JDOHelper. This instance is thread-safe.
*
* @since 2.1
* @return the thread-safe singleton JDOHelper
*/
public static JDOHelper getInstance() {
return INSTANCE;
}
/**
* Some applications might prefer to use instance methods instead of static methods.
*
* @since 2.1
*/
public JDOHelper() {}
/**
* The stateless instance used for handling non-binary-compatible implementations of
* getPersistenceManager.
*/
static final StateInterrogationObjectReturn getPersistenceManager =
(pc, si) -> si.getPersistenceManager(pc);
/**
* The stateless instance used for handling non-binary-compatible implementations of getObjectId.
*/
static final StateInterrogationObjectReturn getObjectId = (pc, si) -> si.getObjectId(pc);
/**
* The stateless instance used for handling non-binary-compatible implementations of
* getTransactionalObjectId.
*/
static final StateInterrogationObjectReturn getTransactionalObjectId =
(pc, si) -> si.getTransactionalObjectId(pc);
/**
* The stateless instance used for handling non-binary-compatible implementations of getVersion.
*/
static final StateInterrogationObjectReturn getVersion = (pc, si) -> si.getVersion(pc);
/**
* The stateless instance used for handling non-binary-compatible implementations of isPersistent.
*/
static final StateInterrogationBooleanReturn isPersistent = (pc, si) -> si.isPersistent(pc);
/**
* The stateless instance used for handling non-binary-compatible implementations of
* isTransactional.
*/
static final StateInterrogationBooleanReturn isTransactional = (pc, si) -> si.isTransactional(pc);
/** The stateless instance used for handling non-binary-compatible implementations of isDirty. */
static final StateInterrogationBooleanReturn isDirty = (pc, si) -> si.isDirty(pc);
/** The stateless instance used for handling non-binary-compatible implementations of isNew. */
static final StateInterrogationBooleanReturn isNew = (pc, si) -> si.isNew(pc);
/**
* The stateless instance used for handling non-binary-compatible implementations of isDeleted.
*/
static final StateInterrogationBooleanReturn isDeleted = (pc, si) -> si.isDeleted(pc);
/**
* The stateless instance used for handling non-binary-compatible implementations of isDetached.
*/
static final StateInterrogationBooleanReturn isDetached = (pc, si) -> si.isDetached(pc);
/**
* Return the associated <code>PersistenceManager</code> if there is one. Transactional and
* persistent instances return the associated <code>PersistenceManager</code>.
*
* <p>Transient non-transactional instances and instances of classes that do not implement <code>
* PersistenceCapable</code> return <code>null</code>.
*
* @see PersistenceCapable#jdoGetPersistenceManager()
* @param pc the <code>PersistenceCapable</code> instance.
* @return the <code>PersistenceManager</code> associated with the parameter instance.
*/
public static PersistenceManager getPersistenceManager(Object pc) {
if (pc instanceof PersistenceCapable) {
return ((PersistenceCapable) pc).jdoGetPersistenceManager();
} else {
return (PersistenceManager) IMPL_HELPER.nonBinaryCompatibleGet(pc, getPersistenceManager);
}
}
/**
* Explicitly mark the parameter instance and field dirty. Normally, <code>PersistenceCapable
* </code> classes are able to detect changes made to their fields. However, if a reference to an
* array is given to a method outside the class, and the array is modified, then the persistent
* instance is not aware of the change. This API allows the application to notify the instance
* that a change was made to a field.
*
* <p>Transient instances and instances of classes that do not implement <code>PersistenceCapable
* </code> ignore this method.
*
* @see PersistenceCapable#jdoMakeDirty(String fieldName)
* @param pc the <code>PersistenceCapable</code> instance.
* @param fieldName the name of the field to be marked dirty.
*/
public static void makeDirty(Object pc, String fieldName) {
if (pc instanceof PersistenceCapable) {
((PersistenceCapable) pc).jdoMakeDirty(fieldName);
} else {
IMPL_HELPER.nonBinaryCompatibleMakeDirty(pc, fieldName);
}
}
/**
* Return a copy of the JDO identity associated with the parameter instance.
*
* <p>Persistent instances of <code>PersistenceCapable</code> classes have a JDO identity managed
* by the <code>PersistenceManager</code>. This method returns a copy of the ObjectId that
* represents the JDO identity.
*
* <p>Transient instances and instances of classes that do not implement <code>PersistenceCapable
* </code> return <code>null</code>.
*
* <p>The ObjectId may be serialized and later restored, and used with a <code>PersistenceManager
* </code> from the same JDO implementation to locate a persistent instance with the same data
* store identity.
*
* <p>If the JDO identity is managed by the application, then the ObjectId may be used with a
* <code>PersistenceManager</code> from any JDO implementation that supports the <code>
* PersistenceCapable</code> class.
*
* <p>If the JDO identity is not managed by the application or the data store, then the ObjectId
* returned is only valid within the current transaction.
*
* <p>
*
* @see PersistenceManager#getObjectId(Object pc)
* @see PersistenceCapable#jdoGetObjectId()
* @see PersistenceManager#getObjectById(Object oid, boolean validate)
* @param pc the PersistenceCapable instance.
* @return a copy of the ObjectId of the parameter instance as of the beginning of the
* transaction.
*/
public static Object getObjectId(Object pc) {
if (pc instanceof PersistenceCapable) {
return ((PersistenceCapable) pc).jdoGetObjectId();
} else {
return IMPL_HELPER.nonBinaryCompatibleGet(pc, getObjectId);
}
}
/**
* Get object ids for a collection of instances. For each instance in the parameter, the
* getObjectId method is called. This method returns one identity instance for each element in the
* parameter. The order of iteration of the returned Collection exactly matches the order of
* iteration of the parameter Collection.
*
* @param pcs the persistence-capable instances
* @return the object ids of the parameters
* @see #getObjectId(Object pc)
* @see #getObjectIds(Object[] pcs)
* @since 2.0
*/
public static Collection<Object> getObjectIds(Collection<?> pcs) {
ArrayList<Object> result = new ArrayList<>();
for (Iterator<?> it = pcs.iterator(); it.hasNext(); ) {
result.add(getObjectId(it.next()));
}
return result;
}
/**
* Get object ids for an array of instances. For each instance in the parameter, the getObjectId
* method is called. This method returns one identity instance for each element in the parameter.
* The order of instances of the returned array exactly matches the order of instances of the
* parameter array.
*
* @param pcs the persistence-capable instances
* @return the object ids of the parameters
* @see #getObjectId(Object pc)
* @see #getObjectIds(Collection pcs)
* @since 2.0
*/
public static Object[] getObjectIds(Object[] pcs) {
Object[] result = new Object[pcs.length];
for (int i = 0; i < pcs.length; ++i) {
result[i] = getObjectId(pcs[i]);
}
return result;
}
/**
* Return a copy of the JDO identity associated with the parameter instance.
*
* @see PersistenceCapable#jdoGetTransactionalObjectId()
* @see PersistenceManager#getObjectById(Object oid, boolean validate)
* @param pc the <code>PersistenceCapable</code> instance.
* @return a copy of the ObjectId of the parameter instance as modified in this transaction.
*/
public static Object getTransactionalObjectId(Object pc) {
if (pc instanceof PersistenceCapable) {
return ((PersistenceCapable) pc).jdoGetTransactionalObjectId();
} else {
return IMPL_HELPER.nonBinaryCompatibleGet(pc, getTransactionalObjectId);
}
}
/**
* Return the version of the instance.
*
* @since 2.0
* @param pc the instance
* @return the version of the instance
*/
public static Object getVersion(Object pc) {
if (pc instanceof PersistenceCapable) {
return ((PersistenceCapable) pc).jdoGetVersion();
} else {
return IMPL_HELPER.nonBinaryCompatibleGet(pc, getVersion);
}
}
/**
* Tests whether the parameter instance is dirty.
*
* <p>Instances that have been modified, deleted, or newly made persistent in the current
* transaction return <code>true</code>.
*
* <p>Transient instances and instances of classes that do not implement <code>PersistenceCapable
* </code> return <code>false</code>.
*
* <p>
*
* @see javax.jdo.spi.StateManager#makeDirty(PersistenceCapable pc, String fieldName)
* @see PersistenceCapable#jdoIsDirty()
* @param pc the <code>PersistenceCapable</code> instance.
* @return <code>true</code> if the parameter instance has been modified in the current
* transaction.
*/
public static boolean isDirty(Object pc) {
if (pc instanceof PersistenceCapable) {
return ((PersistenceCapable) pc).jdoIsDirty();
} else {
return IMPL_HELPER.nonBinaryCompatibleIs(pc, isDirty);
}
}
/**
* Tests whether the parameter instance is transactional.
*
* <p>Instances whose state is associated with the current transaction return true.
*
* <p>Transient instances and instances of classes that do not implement <code>PersistenceCapable
* </code> return <code>false</code>.
*
* @see PersistenceCapable#jdoIsTransactional()
* @param pc the <code>PersistenceCapable</code> instance.
* @return <code>true</code> if the parameter instance is transactional.
*/
public static boolean isTransactional(Object pc) {
if (pc instanceof PersistenceCapable) {
return ((PersistenceCapable) pc).jdoIsTransactional();
} else {
return IMPL_HELPER.nonBinaryCompatibleIs(pc, isTransactional);
}
}
/**
* Tests whether the parameter instance is persistent.
*
* <p>Instances that represent persistent objects in the data store return <code>true</code>.
*
* <p>Transient instances and instances of classes that do not implement <code>PersistenceCapable
* </code> return <code>false</code>.
*
* <p>
*
* @see PersistenceManager#makePersistent(Object pc)
* @see PersistenceCapable#jdoIsPersistent()
* @param pc the <code>PersistenceCapable</code> instance.
* @return <code>true</code> if the parameter instance is persistent.
*/
public static boolean isPersistent(Object pc) {
if (pc instanceof PersistenceCapable) {
return ((PersistenceCapable) pc).jdoIsPersistent();
} else {
return IMPL_HELPER.nonBinaryCompatibleIs(pc, isPersistent);
}
}
/**
* Tests whether the parameter instance has been newly made persistent.
*
* <p>Instances that have been made persistent in the current transaction return <code>true</code>
* .
*
* <p>Transient instances and instances of classes that do not implement <code>PersistenceCapable
* </code> return <code>false</code>.
*
* <p>
*
* @see PersistenceManager#makePersistent(Object pc)
* @see PersistenceCapable#jdoIsNew()
* @param pc the <code>PersistenceCapable</code> instance.
* @return <code>true</code> if the parameter instance was made persistent in the current
* transaction.
*/
public static boolean isNew(Object pc) {
if (pc instanceof PersistenceCapable) {
return ((PersistenceCapable) pc).jdoIsNew();
} else {
return IMPL_HELPER.nonBinaryCompatibleIs(pc, isNew);
}
}
/**
* Tests whether the parameter instance has been deleted.
*
* <p>Instances that have been deleted in the current transaction return <code>true</code>.
*
* <p>Transient instances and instances of classes that do not implement <code>PersistenceCapable
* </code> return <code>false</code>.
*
* <p>
*
* @see PersistenceManager#deletePersistent(Object pc)
* @see PersistenceCapable#jdoIsDeleted()
* @param pc the <code>PersistenceCapable</code> instance.
* @return <code>true</code> if the parameter instance was deleted in the current transaction.
*/
public static boolean isDeleted(Object pc) {
if (pc instanceof PersistenceCapable) {
return ((PersistenceCapable) pc).jdoIsDeleted();
} else {
return IMPL_HELPER.nonBinaryCompatibleIs(pc, isDeleted);
}
}
/**
* Tests whether the parameter instance has been detached.
*
* <p>Instances that have been detached return true.
*
* <p>Transient instances return false.
*
* <p>
*
* @see PersistenceCapable#jdoIsDetached()
* @return <code>true</code> if this instance is detached.
* @since 2.0
* @param pc the instance
*/
public static boolean isDetached(Object pc) {
if (pc instanceof PersistenceCapable) {
return ((PersistenceCapable) pc).jdoIsDetached();
} else {
return IMPL_HELPER.nonBinaryCompatibleIs(pc, isDetached);
}
}
/**
* Accessor for the state of the passed object.
*
* @param pc The object
* @return The object state
* @since 2.1
*/
public static ObjectState getObjectState(Object pc) {
if (pc == null) {
return null;
}
if (isDetached(pc)) {
if (isDirty(pc)) {
// Detached Dirty
return ObjectState.DETACHED_DIRTY;
} else {
// Detached Not Dirty
return ObjectState.DETACHED_CLEAN;
}
} else {
if (isPersistent(pc)) {
if (isTransactional(pc)) {
if (isDirty(pc)) {
if (isNew(pc)) {
if (isDeleted(pc)) {
// Persistent Transactional Dirty New Deleted
return ObjectState.PERSISTENT_NEW_DELETED;
} else {
// Persistent Transactional Dirty New Not Deleted
return ObjectState.PERSISTENT_NEW;
}
} else {
if (isDeleted(pc)) {
// Persistent Transactional Dirty Not New Deleted
return ObjectState.PERSISTENT_DELETED;
} else {
// Persistent Transactional Dirty Not New Not Deleted
return ObjectState.PERSISTENT_DIRTY;
}
}
} else {
// Persistent Transactional Not Dirty
return ObjectState.PERSISTENT_CLEAN;
}
} else {
if (isDirty(pc)) {
// Persistent Nontransactional Dirty
return ObjectState.PERSISTENT_NONTRANSACTIONAL_DIRTY;
} else {
// Persistent Nontransactional Not Dirty
return ObjectState.HOLLOW_PERSISTENT_NONTRANSACTIONAL;
}
}
} else {
if (isTransactional(pc)) {
if (isDirty(pc)) {
// Not Persistent Transactional Dirty
return ObjectState.TRANSIENT_DIRTY;
} else {
// Not Persistent Transactional Not Dirty
return ObjectState.TRANSIENT_CLEAN;
}
} else {
// Not Persistent Not Transactional
return ObjectState.TRANSIENT;
}
}
}
}
/**
* Get the anonymous <code>PersistenceManagerFactory</code> configured via the standard
* configuration file resource "META-INF/jdoconfig.xml", using the current thread's context class
* loader to locate the configuration file resource(s).
*
* @return the anonymous <code>PersistenceManagerFactory</code>.
* @since 2.1
* @see #getPersistenceManagerFactory(Map,String,ClassLoader,ClassLoader)
*/
public static PersistenceManagerFactory getPersistenceManagerFactory() {
ClassLoader cl = getContextClassLoader();
return getPersistenceManagerFactory(null, ANONYMOUS_PERSISTENCE_MANAGER_FACTORY_NAME, cl, cl);
}
/**
* Get the anonymous <code>PersistenceManagerFactory</code> configured via the standard
* configuration file resource "META-INF/jdoconfig.xml", using the given class loader.
*
* @return the anonymous <code>PersistenceManagerFactory</code>.
* @param pmfClassLoader the ClassLoader used to load resources and classes
* @since 2.1
* @see #getPersistenceManagerFactory(Map,String,ClassLoader,ClassLoader)
*/
public static PersistenceManagerFactory getPersistenceManagerFactory(ClassLoader pmfClassLoader) {
return getPersistenceManagerFactory(
null, ANONYMOUS_PERSISTENCE_MANAGER_FACTORY_NAME, pmfClassLoader, pmfClassLoader);
}
/**
* Get a <code>PersistenceManagerFactory</code> based on a <code>Properties</code> instance, using
* the current thread's context class loader to locate the <code>PersistenceManagerFactory</code>
* class.
*
* @return the <code>PersistenceManagerFactory</code>.
* @param props a <code>Properties</code> instance with properties of the <code>
* PersistenceManagerFactory</code>.
* @see #getPersistenceManagerFactory(java.util.Map,ClassLoader)
*/
public static PersistenceManagerFactory getPersistenceManagerFactory(Map<?, ?> props) {
return getPersistenceManagerFactory(null, props, getContextClassLoader());
}
/**
* Get a <code>PersistenceManagerFactory</code> based on a <code>Map</code> and a class loader.
* This method delegates to the getPersistenceManagerFactory method that takes a Map of overrides
* and a Map of properties, passing null as the overrides parameter.
*
* @see #getPersistenceManagerFactory(java.util.Map, java.util.Map, ClassLoader)
* @return the <code>PersistenceManagerFactory</code>.
* @param props a <code>Map</code> with properties of the <code>PersistenceManagerFactory</code>.
* @param pmfClassLoader the class loader used to load the <code>PersistenceManagerFactory</code>
* class
* @since 1.0
*/
public static PersistenceManagerFactory getPersistenceManagerFactory(
Map<?, ?> props, ClassLoader pmfClassLoader) {
return getPersistenceManagerFactory(null, props, pmfClassLoader);
}
/**
* Get a <code>PersistenceManagerFactory</code> based on a <code>Map</code> of overrides, a <code>
* Map</code> of properties, and a class loader. The following are standard key names: <br>
* <code>"javax.jdo.PersistenceManagerFactoryClass"
* <BR>"javax.jdo.option.Optimistic",
* <BR>"javax.jdo.option.RetainValues",
* <BR>"javax.jdo.option.RestoreValues",
* <BR>"javax.jdo.option.IgnoreCache",
* <BR>"javax.jdo.option.NontransactionalRead",
* <BR>"javax.jdo.option.NontransactionalWrite",
* <BR>"javax.jdo.option.Multithreaded",
* <BR>"javax.jdo.option.ConnectionUserName",
* <BR>"javax.jdo.option.ConnectionPassword",
* <BR>"javax.jdo.option.ConnectionURL",
* <BR>"javax.jdo.option.ConnectionFactoryName",
* <BR>"javax.jdo.option.ConnectionFactory2Name",
* <BR>"javax.jdo.option.Mapping",
* <BR>"javax.jdo.mapping.Catalog",
* <BR>"javax.jdo.mapping.Schema",
* <BR>"javax.jdo.option.PersistenceUnitName",
* <BR>"javax.jdo.option.DetachAllOnCommit",
* <BR>"javax.jdo.option.CopyOnAttach",
* <BR>"javax.jdo.option.ReadOnly",
* <BR>"javax.jdo.option.TransactionIsolationLevel",
* <BR>"javax.jdo.option.TransactionType",
* <BR>"javax.jdo.option.ServerTimeZoneID",
* <BR>"javax.jdo.option.DatastoreReadTimeoutMillis",
* <BR>"javax.jdo.option.DatastoreWriteTimeoutMillis",
* <BR>"javax.jdo.option.Name".
* </code> and properties of the form <br>
* <code>javax.jdo.option.InstanceLifecycleListener.{listenerClass}[=[{pcClasses}]]</code> where
* <code>{listenerClass}</code> is the fully qualified name of a class that implements {@link
* javax.jdo.listener.InstanceLifecycleListener}, and <code>{pcClasses}</code> is an optional
* comma- or whitespace-delimited list of persistence-capable classes to be observed; the absence
* of a value for a property of this form means that instances of all persistence-capable classes
* will be observed by an instance of the given listener class.
*
* <p>JDO implementations are permitted to define key values of their own. Any key values not
* recognized by the implementation must be ignored. Key values that are recognized but not
* supported by an implementation must result in a <code>JDOFatalUserException</code> thrown by
* the method.
*
* <p>The returned <code>PersistenceManagerFactory</code> is not configurable (the <code>
* set<I>XXX</I></code> methods will throw an exception).
*
* <p>JDO implementations might manage a map of instantiated <code>PersistenceManagerFactory
* </code> instances based on specified property key values, and return a previously instantiated
* <code>PersistenceManagerFactory</code> instance. In this case, the properties of the returned
* instance must exactly match the requested properties.
*
* @param overrides Overrides of properties
* @param props a <code>Properties</code> instance with properties of the <code>
* PersistenceManagerFactory</code>.
* @param pmfClassLoader the class loader to use to load the <code>PersistenceManagerFactory
* </code> class
* @return the <code>PersistenceManagerFactory</code>.
* @throws JDOFatalUserException if
* <ul>
* <li>the pmfClassLoader passed is invalid; or
* <li>a valid class name cannot be obtained from either <code>props</code> or system
* resources (an entry in META-INF/services/javax.jdo.PersistenceManagerFactory); or
* <li>all implementations throw an exception.
* </ul>
*
* @since 2.1
*/
protected static PersistenceManagerFactory getPersistenceManagerFactory(
Map<?, ?> overrides, Map<?, ?> props, ClassLoader pmfClassLoader) {
List<Throwable> exceptions = new ArrayList<>();
if (pmfClassLoader == null)
throw new JDOFatalUserException(MSG.msg("EXC_GetPMFNullLoader")); // NOI18N
JDOImplHelper.assertOnlyKnownStandardProperties(overrides);
JDOImplHelper.assertOnlyKnownStandardProperties(props);
// first try to get the class name from the properties object.
String pmfClassName = (String) props.get(PROPERTY_PERSISTENCE_MANAGER_FACTORY_CLASS);
if (!isNullOrBlank(pmfClassName)) {
// a valid name was returned from the properties.
return invokeGetPersistenceManagerFactoryOnImplementation(
pmfClassName, overrides, props, pmfClassLoader);
} else {
/*
* If you have a jar file that provides the jdo implementation,
* a file naming the implementation goes into the file
* packaged into the jar file, called
* META-INF/services/javax.jdo.PersistenceManagerFactory.
* The contents of the file is a string that is the PMF class name,
* null or blank.
* For each file in pmfClassLoader named
* META-INF/services/javax.jdo.PersistenceManagerFactory,
* this method will try to invoke the getPersistenceManagerFactory
* method of the implementation class.
* Return the factory if a valid class name is extracted from
* resources and the invocation returns an instance.
* Otherwise add the exception thrown to
* an exception list.
*/
Enumeration<URL> urls = null;
try {
urls = getResources(pmfClassLoader, SERVICE_LOOKUP_PMF_RESOURCE_NAME);
} catch (Throwable ex) {
exceptions.add(ex);
}
if (urls != null) {
while (urls.hasMoreElements()) {
try {
pmfClassName = getClassNameFromURL(urls.nextElement());
// return the implementation that is valid.
PersistenceManagerFactory pmf =
invokeGetPersistenceManagerFactoryOnImplementation(
pmfClassName, overrides, props, pmfClassLoader);
return pmf;
} catch (Throwable ex) {
// remember exceptions from failed pmf invocations
exceptions.add(ex);
}
}
}
}
// no PMF class name in props and no services.
throw new JDOFatalUserException(
MSG.msg("EXC_GetPMFNoPMFClassNamePropertyOrPUNameProperty"),
exceptions.toArray(new Throwable[exceptions.size()]));
}
/**
* Get a class name from a URL. The URL is from getResources with e.g.
* META-INF/services/javax.jdo.PersistenceManagerFactory as the parameter. Parse the file,
* removing blank lines, comment lines, and comments.
*
* @param url the URL of the services file
* @return the name of the class contained in the file
* @throws java.io.IOException Throw if an error occurs on accessing this URL
* @since 2.1
*/
protected static String getClassNameFromURL(URL url) throws IOException {
InputStream is = openStream(url);
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line = null;
try {
while ((line = reader.readLine()) != null) {
line = line.trim();
if (line.length() == 0 || line.startsWith("#")) {
continue;
}
// else assume first line of text is the PMF class name
String[] tokens = line.split("\\s");
String pmfClassName = tokens[0];
int indexOfComment = pmfClassName.indexOf("#");
if (indexOfComment == -1) {
return pmfClassName;
}
// else pmfClassName has a comment at the end of it -- remove
return pmfClassName.substring(0, indexOfComment);
}
return null;
} finally {
try {
reader.close();
} catch (IOException x) {
// gulp
}
}
}
/**
* Returns a named {@link PersistenceManagerFactory} or persistence unit.
*
* @since 2.1
* @see #getPersistenceManagerFactory(Map,String,ClassLoader,ClassLoader)
* @param name Name of the PMF
* @return PersistenceManagerFactory
*/
public static PersistenceManagerFactory getPersistenceManagerFactory(String name) {
ClassLoader cl = getContextClassLoader();
return getPersistenceManagerFactory(null, name, cl, cl);
}
/**
* Returns a named {@link PersistenceManagerFactory} or persistence unit.
*
* @since 1.0
* @see #getPersistenceManagerFactory(Map,String,ClassLoader,ClassLoader)
* @param name Name of the PMF
* @param loader ClassLoader to use
* @return PersistenceManagerFactory
*/
public static PersistenceManagerFactory getPersistenceManagerFactory(
String name, ClassLoader loader) {
return getPersistenceManagerFactory(null, name, loader, loader);
}
/**
* Returns a named {@link PersistenceManagerFactory} or persistence unit.
*
* @since 2.0
* @see #getPersistenceManagerFactory(Map,String,ClassLoader,ClassLoader)
* @param name Name of the PMF
* @param resourceLoader ClassLoader to use for loading resources
* @param pmfLoader ClassLoader to use for loading the PMF
* @return PersistenceManagerFactory
*/
public static PersistenceManagerFactory getPersistenceManagerFactory(
String name, ClassLoader resourceLoader, ClassLoader pmfLoader) {
return getPersistenceManagerFactory(null, name, resourceLoader, pmfLoader);
}
/**
* Returns a named {@link PersistenceManagerFactory} or persistence unit.
*
* @since 2.1
* @see #getPersistenceManagerFactory(Map,String,ClassLoader,ClassLoader)
* @param name Name of the PMF
* @param overrides Property overrides
* @return PersistenceManagerFactory
*/
public static PersistenceManagerFactory getPersistenceManagerFactory(
Map<?, ?> overrides, String name) {
ClassLoader cl = getContextClassLoader();
return getPersistenceManagerFactory(overrides, name, cl, cl);
}
/**
* Returns a named {@link PersistenceManagerFactory} or persistence unit.
*
* @since 2.1
* @see #getPersistenceManagerFactory(Map,String,ClassLoader,ClassLoader)
* @param overrides Property overrides
* @param name Name of the PMF
* @param resourceLoader ClassLoader to use for loading resources
* @return The PMF
*/
public static PersistenceManagerFactory getPersistenceManagerFactory(
Map<?, ?> overrides, String name, ClassLoader resourceLoader) {
return getPersistenceManagerFactory(overrides, name, resourceLoader, resourceLoader);
}
/**
* Returns a {@link PersistenceManagerFactory} configured based on the properties stored in the
* resource at <code>name</code>, or, if not found, returns a {@link PersistenceManagerFactory}
* with the given name or, if not found, returns a <code>javax.persistence.EntityManagerFactory
* </code> cast to a {@link PersistenceManagerFactory}. If the name given is null or consists only
* of whitespace, it is interpreted as {@link
* Constants#ANONYMOUS_PERSISTENCE_MANAGER_FACTORY_NAME}. The following are standard key names:
* <br>
* <code>"javax.jdo.PersistenceManagerFactoryClass"
* <BR>"javax.jdo.option.Optimistic",
* <BR>"javax.jdo.option.RetainValues",
* <BR>"javax.jdo.option.RestoreValues",
* <BR>"javax.jdo.option.IgnoreCache",
* <BR>"javax.jdo.option.NontransactionalRead",
* <BR>"javax.jdo.option.NontransactionalWrite",
* <BR>"javax.jdo.option.Multithreaded",
* <BR>"javax.jdo.option.ConnectionUserName",
* <BR>"javax.jdo.option.ConnectionPassword",
* <BR>"javax.jdo.option.ConnectionURL",
* <BR>"javax.jdo.option.ConnectionFactoryName",
* <BR>"javax.jdo.option.ConnectionFactory2Name",
* <BR>"javax.jdo.option.Mapping",
* <BR>"javax.jdo.mapping.Catalog",
* <BR>"javax.jdo.mapping.Schema",
* <BR>"javax.jdo.option.PersistenceUnitName".
* <BR>"javax.jdo.option.DetachAllOnCommit".
* <BR>"javax.jdo.option.CopyOnAttach".
* <BR>"javax.jdo.option.TransactionType".
* <BR>"javax.jdo.option.ServerTimeZoneID".
* <BR>"javax.jdo.option.DatastoreReadTimeoutMillis",
* <BR>"javax.jdo.option.DatastoreWriteTimeoutMillis",
* <BR>"javax.jdo.option.Name".
* </code> and properties of the form <br>
* <code>javax.jdo.option.InstanceLifecycleListener.{listenerClass}[=[{pcClasses}]]</code> where
* <code>{listenerClass}</code> is the fully qualified name of a class that implements {@link
* javax.jdo.listener.InstanceLifecycleListener}, and <code>{pcClasses}</code> is an optional
* comma- or whitespace-delimited list of persistence-capable classes to be observed; the absence
* of a value for a property of this form means that instances of all persistence-capable classes
* will be observed by an instance of the given listener class.
*
* <p>JDO implementations are permitted to define key values of their own. Any key values not
* recognized by the implementation must be ignored. Key values that are recognized but not
* supported by an implementation must result in a <code>JDOFatalUserException</code> thrown by
* the method.
*
* <p>The returned <code>PersistenceManagerFactory</code> is not configurable (the <code>
* set<I>XXX</I></code> methods will throw an exception).
*
* <p>This method loads the properties found at <code>name</code>, if any, via <code>
* resourceLoader</code>, and creates a {@link PersistenceManagerFactory} with <code>pmfLoader
* </code>. Any exceptions thrown during resource loading will be wrapped in a {@link
* JDOFatalUserException}. If multiple PMFs with the requested name are found, a {@link
* JDOFatalUserException} is thrown.
*
* @since 2.1
* @param overrides a Map containing properties that override properties defined in any resources
* loaded according to the "name" parameter
* @param name interpreted as the name of the resource containing the PMF properties, the name of
* the PMF, or the persistence unit name, in that order; if name is null, blank or whitespace,
* it is interpreted as indicating the anonymous {@link PersistenceManagerFactory}.
* @param resourceLoader the class loader to use to load properties file resources; must be
* non-null if <code>name</code> is non-null or blank
* @param pmfLoader the class loader to use to load the {@link PersistenceManagerFactory} or
* <code>javax.persistence.EntityManagerFactory</code> classes
* @return the {@link PersistenceManagerFactory} with properties in the given resource, with the
* given name, or with the given persitence unit name
* @see Constants#ANONYMOUS_PERSISTENCE_MANAGER_FACTORY_NAME
*/
public static PersistenceManagerFactory getPersistenceManagerFactory(
Map<?, ?> overrides, String name, ClassLoader resourceLoader, ClassLoader pmfLoader) {
if (pmfLoader == null)
throw new JDOFatalUserException(MSG.msg("EXC_GetPMFNullPMFLoader")); // NOI18N
if (resourceLoader == null) {
throw new JDOFatalUserException(MSG.msg("EXC_GetPMFNullPropsLoader")); // NOI18N
}
Map<Object, Object> props = null;
// trim spaces from name and ensure non-null
name = (name == null ? ANONYMOUS_PERSISTENCE_MANAGER_FACTORY_NAME : name.trim());
if (!ANONYMOUS_PERSISTENCE_MANAGER_FACTORY_NAME.equals(name)) {
props = loadPropertiesFromResource(resourceLoader, name);
}
if (props != null) {
// add the SPI property to inform the implementation that
// the PMF was configured by the given resource name
// and not via named PMF for proper deserialization
props.put(PROPERTY_SPI_RESOURCE_NAME, name);
props.remove(PROPERTY_NAME);
return getPersistenceManagerFactory(overrides, props, pmfLoader);
}
// props were null; try getting from jdoconfig.xml
props = getPropertiesFromJdoconfig(name, resourceLoader);
if (props != null) {
// inform the impl that the config came from a jdoconfig.xml
// element with the given name
props.put(PROPERTY_NAME, name);
props.remove(PROPERTY_SPI_RESOURCE_NAME);
// we have loaded a Properties, delegate to implementation
return getPersistenceManagerFactory(overrides, props, pmfLoader);
}
// no properties found; last try to see if name is a JPA PU name
if (!ANONYMOUS_PERSISTENCE_MANAGER_FACTORY_NAME.equals(name)) {
props = new Properties();
props.put(PROPERTY_PERSISTENCE_UNIT_NAME, name);
return getPersistenceManagerFactory(overrides, props, pmfLoader);
}
// no PMF found; give up
throw new JDOFatalUserException(
MSG.msg("EXC_NoPMFConfigurableViaPropertiesOrXML", name)); // NOI18N
}
/**
* Invoke the getPersistenceManagerFactory method on the implementation. If the overrides
* parameter to this method is not null, the static method with Map overrides, Map properties
* parameters will be invoked. If the overrides parameter to this method is null, the static
* method with Map properties parameter will be invoked.
*
* @param pmfClassName the name of the implementation factory class
* @param overrides a Map of overrides
* @param properties a Map of properties
* @param cl the class loader to use to load the implementation class
* @return the PersistenceManagerFactory
*/
protected static PersistenceManagerFactory invokeGetPersistenceManagerFactoryOnImplementation(
String pmfClassName, Map<?, ?> overrides, Map<?, ?> properties, ClassLoader cl) {
if (overrides != null) {
// overrides is not null; use getPersistenceManagerFactory(Map overrides, Map props)
try {
Class<?> implClass = forName(pmfClassName, true, cl);
Method m =
getMethod(
implClass,
"getPersistenceManagerFactory", // NOI18N
new Class[] {Map.class, Map.class});
PersistenceManagerFactory pmf =
(PersistenceManagerFactory) invoke(m, null, new Object[] {overrides, properties});
if (pmf == null) {
throw new JDOFatalInternalException(MSG.msg("EXC_GetPMFNullPMF", pmfClassName)); // NOI18N
}
return pmf;
} catch (ClassNotFoundException e) {
throw new JDOFatalUserException(
MSG.msg("EXC_GetPMFClassNotFound", pmfClassName), e); // NOI18N
} catch (NoSuchMethodException e) {
throw new JDOFatalInternalException(
MSG.msg("EXC_GetPMFNoSuchMethod2", pmfClassName), e); // NOI18N
} catch (NullPointerException e) {
throw new JDOFatalInternalException(
MSG.msg("EXC_GetPMFNullPointerException", pmfClassName), e); // NOI18N
} catch (IllegalAccessException e) {
throw new JDOFatalUserException(
MSG.msg("EXC_GetPMFIllegalAccess", pmfClassName), e); // NOI18N
} catch (ClassCastException e) {
throw new JDOFatalInternalException(
MSG.msg("EXC_GetPMFClassCastException", pmfClassName), e); // NOI18N
} catch (InvocationTargetException ite) {
Throwable nested = ite.getTargetException();
if (nested instanceof JDOException) {
throw (JDOException) nested;
} else
throw new JDOFatalInternalException(
MSG.msg("EXC_GetPMFUnexpectedException"), ite); // NOI18N
}
} else {
// overrides is null; use getPersistenceManagerFactory(Map props)
try {
Class<?> implClass = forName(pmfClassName, true, cl);
Method m =
getMethod(
implClass,
"getPersistenceManagerFactory", // NOI18N
new Class[] {Map.class});
PersistenceManagerFactory pmf =
(PersistenceManagerFactory) invoke(m, null, new Object[] {properties});
if (pmf == null) {
throw new JDOFatalInternalException(MSG.msg("EXC_GetPMFNullPMF", pmfClassName)); // NOI18N
}
return pmf;
} catch (ClassNotFoundException e) {
throw new JDOFatalUserException(
MSG.msg("EXC_GetPMFClassNotFound", pmfClassName), e); // NOI18N
} catch (NoSuchMethodException e) {
throw new JDOFatalInternalException(
MSG.msg("EXC_GetPMFNoSuchMethod", pmfClassName), e); // NOI18N
} catch (NullPointerException e) {
throw new JDOFatalInternalException(
MSG.msg("EXC_GetPMFNullPointerException", pmfClassName), e); // NOI18N
} catch (IllegalAccessException e) {
throw new JDOFatalUserException(
MSG.msg("EXC_GetPMFIllegalAccess", pmfClassName), e); // NOI18N
} catch (ClassCastException e) {
throw new JDOFatalInternalException(
MSG.msg("EXC_GetPMFClassCastException", pmfClassName), e); // NOI18N
} catch (InvocationTargetException ite) {
Throwable nested = ite.getTargetException();
if (nested instanceof JDOException) {
throw (JDOException) nested;
} else
throw new JDOFatalInternalException(
MSG.msg("EXC_GetPMFUnexpectedException"), ite); // NOI18N
}
}
}
/**
* Load a Properties instance by name from the class loader.
*
* @param resourceLoader the class loader from which to load the properties
* @param name the name of the resource
* @return a Properties instance or null if no resource is found
*/
protected static Map<Object, Object> loadPropertiesFromResource(
ClassLoader resourceLoader, String name) {
InputStream in = null;
Properties props = null;
// try to load resources from properties file
try {
in = getResourceAsStream(resourceLoader, name);
if (in != null) {
// then some kind of resource was found by the given name;
// assume that it's a properties file
props = new Properties();
props.load(in);
}
} catch (IOException ioe) {
throw new JDOFatalUserException(MSG.msg("EXC_GetPMFIOExceptionRsrc", name), ioe); // NOI18N
} finally {
if (in != null) {
try {
in.close();
} catch (IOException ioe) {
}
}
}
return props;
}
/**
* @see #getNamedPMFProperties(String,ClassLoader,String)
* @since 2.1
* @param name Name of the PMF
* @param resourceLoader ClassLoader to use for loading resources
* @return The properties for this PMF
*/
protected static Map<Object, Object> getPropertiesFromJdoconfig(
String name, ClassLoader resourceLoader) {
return getNamedPMFProperties(name, resourceLoader, JDOCONFIG_RESOURCE_NAME);
}
/**
* Find and return the named {@link PersistenceManagerFactory}'s properties, or null if not found.
* If multiple named PMF property sets with the given name are found (including anonymous ones),
* throw {@link JDOFatalUserException}. This method is here only to facilitate testing; the
* parameter "jdoconfigResourceName" in public usage should always have the value given in the
* constant {@link Constants#JDOCONFIG_RESOURCE_NAME}.
*
* @param name The persistence unit name; null is disallowed.
* @param resourceLoader The ClassLoader used to load the standard JDO configuration file.
* @param jdoconfigResourceName The name of the configuration file to read. In public usage, this
* should always be the value of {@link Constants#JDOCONFIG_RESOURCE_NAME}.
* @return The named <code>PersistenceManagerFactory</code> properties if found, null if not.
* @since 2.1
* @throws JDOFatalUserException if multiple named PMF property sets are found with the given
* name, or any other exception is encountered.
*/
protected static Map<Object, Object> getNamedPMFProperties(
String name, ClassLoader resourceLoader, String jdoconfigResourceName) {
// key is PU name, value is Map of PU properties
Map<String, Map<Object, Object>> propertiesByNameInAllConfigs = new HashMap<>();
try {
URL firstFoundConfigURL = null;
// get all JDO configurations
Enumeration<URL> resources = getResources(resourceLoader, jdoconfigResourceName);
if (resources.hasMoreElements()) {
ArrayList<URL> processedResources = new ArrayList<>();
// get ready to parse XML
DocumentBuilderFactory factory = getDocumentBuilderFactory();
do {
URL currentConfigURL = resources.nextElement();
if (processedResources.contains(currentConfigURL)) {
continue;
} else {
processedResources.add(currentConfigURL);
}
Map<String, Map<Object, Object>> propertiesByNameInCurrentConfig =
readNamedPMFProperties(currentConfigURL, name, factory);
// try to detect duplicate requested PU
if (propertiesByNameInCurrentConfig.containsKey(name)) {
// possible dup -- check for it
if (firstFoundConfigURL == null) {
firstFoundConfigURL = currentConfigURL;
}
if (propertiesByNameInAllConfigs.containsKey(name))
throw new JDOFatalUserException(
MSG.msg(
"EXC_DuplicateRequestedNamedPMFFoundInDifferentConfigs",
"".equals(name) ? "(anonymous)" : name,
firstFoundConfigURL.toExternalForm(),
currentConfigURL.toExternalForm())); // NOI18N
}
// no dups -- add found PUs to all PUs and keep going
propertiesByNameInAllConfigs.putAll(propertiesByNameInCurrentConfig);
} while (resources.hasMoreElements());
}
} catch (FactoryConfigurationError e) {
throw new JDOFatalUserException(MSG.msg("ERR_NoDocumentBuilderFactory"), e);
} catch (IOException ioe) {
throw new JDOFatalUserException(MSG.msg("EXC_GetPMFIOExceptionRsrc", name), ioe); // NOI18N
}
// done with reading all config resources;
// return what we found, which may very well be null
return propertiesByNameInAllConfigs.get(name);
}
protected static DocumentBuilderFactory getDocumentBuilderFactory() {
@SuppressWarnings("static-access")
DocumentBuilderFactory factory = IMPL_HELPER.getRegisteredDocumentBuilderFactory();
if (factory == null) {
factory = getDefaultDocumentBuilderFactory();
}
return factory;
}
protected static DocumentBuilderFactory getDefaultDocumentBuilderFactory() {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setIgnoringComments(true);
factory.setNamespaceAware(true);
factory.setValidating(false);
factory.setIgnoringElementContentWhitespace(true);
factory.setExpandEntityReferences(true);
return factory;
}
protected static ErrorHandler getErrorHandler() {
@SuppressWarnings("static-access")
ErrorHandler handler = IMPL_HELPER.getRegisteredErrorHandler();
if (handler == null) {
handler = getDefaultErrorHandler();
}
return handler;
}
protected static ErrorHandler getDefaultErrorHandler() {
return new ErrorHandler() {
public void error(SAXParseException exception) throws SAXException {
throw exception;
}
public void fatalError(SAXParseException exception) throws SAXException {
throw exception;
}
public void warning(SAXParseException exception) throws SAXException {
// gulp: ignore warnings
}
};
}
/**
* Reads JDO configuration file, creates a Map for each persistence-manager-factory, then returns
* the map.
*
* @param url URL of a JDO configuration file compliant with javax/jdo/jdoconfig.xsd.
* @param requestedPMFName The name of the requested persistence unit (allows for fail-fast).
* @param factory The <code>DocumentBuilderFactory</code> to use for XML parsing.
* @return a Map&lt;String,Map&gt; holding persistence unit configurations; for the anonymous
* persistence unit, the value of the String key is the empty string, "".
*/
protected static Map<String, Map<Object, Object>> readNamedPMFProperties(
URL url, String requestedPMFName, DocumentBuilderFactory factory) {
requestedPMFName = requestedPMFName == null ? "" : requestedPMFName.trim();
Map<String, Map<Object, Object>> propertiesByName = new HashMap<>();
InputStream in = null;
try {
DocumentBuilder builder = factory.newDocumentBuilder();
builder.setErrorHandler(getErrorHandler());
in = openStream(url);
Document doc = builder.parse(in);
Element root = doc.getDocumentElement();
if (root == null) {
throw new JDOFatalUserException(
MSG.msg("EXC_InvalidJDOConfigNoRoot", url.toExternalForm()));
}
NodeList pmfs = root.getElementsByTagName(ELEMENT_PERSISTENCE_MANAGER_FACTORY);
for (int i = 0; i < pmfs.getLength(); i++) {
Node pmfElement = pmfs.item(i);
Properties pmfPropertiesFromAttributes = readPropertiesFromPMFElementAttributes(pmfElement);
Properties pmfPropertiesFromElements = readPropertiesFromPMFSubelements(pmfElement, url);
// for informative error handling, get name (or names) now
String pmfNameFromAtts = pmfPropertiesFromAttributes.getProperty(PROPERTY_NAME);
String pmfNameFromElem = pmfPropertiesFromElements.getProperty(PROPERTY_NAME);
String pmfName = null;
if (isNullOrBlank(pmfNameFromAtts)) {
// no PMF name attribute given
if (!isNullOrBlank(pmfNameFromElem)) {
// PMF name element was given
pmfName = pmfNameFromElem;
} else {
// PMF name not given at all, means the "anonymous" PMF
pmfName = ANONYMOUS_PERSISTENCE_MANAGER_FACTORY_NAME;
}
} else {
// PMF name given in an attribute
if (!isNullOrBlank(pmfNameFromElem)) {
// exception -- PMF name given as both att & elem
throw new JDOFatalUserException(
MSG.msg(
"EXC_DuplicatePMFNamePropertyFoundWithinConfig",
pmfNameFromAtts,
pmfNameFromElem,
url.toExternalForm()));
}
pmfName = pmfNameFromAtts;
}
pmfName = pmfName == null ? "" : pmfName.trim();
// check for duplicate properties among atts & elems
if (requestedPMFName.equals(pmfName)) {
for (Object o : pmfPropertiesFromAttributes.keySet()) {
String property = (String) o;
if (pmfPropertiesFromElements.contains(property)) {
throw new JDOFatalUserException(
MSG.msg("EXC_DuplicatePropertyFound", property, pmfName, url.toExternalForm()));
}
}
}
// at this point, we're guaranteed not to have duplicate
// properties -- merge them
Properties pmfProps = new Properties();
pmfProps.putAll(pmfPropertiesFromAttributes);
pmfProps.putAll(pmfPropertiesFromElements);
// check for duplicate requested PMF name
if (pmfName.equals(requestedPMFName) && propertiesByName.containsKey(pmfName)) {
throw new JDOFatalUserException(
MSG.msg(
"EXC_DuplicateRequestedNamedPMFFoundInSameConfig",
pmfName,
url.toExternalForm()));
}
propertiesByName.put(pmfName, pmfProps);
}
return propertiesByName;
} catch (IOException ioe) {
throw new JDOFatalUserException(
MSG.msg("EXC_GetPMFIOExceptionRsrc", url.toString()), ioe); // NOI18N
} catch (ParserConfigurationException e) {
throw new JDOFatalInternalException(MSG.msg("EXC_ParserConfigException"), e);
} catch (SAXParseException e) {
throw new JDOFatalUserException(
MSG.msg(
"EXC_SAXParseException",
url.toExternalForm(),
e.getLineNumber(),
e.getColumnNumber()),
e);
} catch (SAXException e) {
throw new JDOFatalUserException(MSG.msg("EXC_SAXException", url.toExternalForm()), e);
} catch (JDOException e) {
throw e;
} catch (RuntimeException e) {
throw new JDOFatalUserException(MSG.msg("EXC_SAXException", url.toExternalForm()), e);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException ioe) {
/* gulp */
}
}
}
}
protected static Properties readPropertiesFromPMFElementAttributes(Node pmfElement) {
Properties p = new Properties();
NamedNodeMap attributes = pmfElement.getAttributes();
if (attributes == null) {
return p;
}
for (int i = 0; i < attributes.getLength(); i++) {
Node att = attributes.item(i);
String attName = att.getNodeName();
String attValue = att.getNodeValue().trim();
String jdoPropertyName = ATTRIBUTE_PROPERTY_XREF.get(attName);
p.put(jdoPropertyName != null ? jdoPropertyName : attName, attValue);
}
return p;
}
protected static Properties readPropertiesFromPMFSubelements(Node pmfElement, URL url) {
Properties p = new Properties();
NodeList elements = pmfElement.getChildNodes();
if (elements == null) {
return p;
}
for (int i = 0; i < elements.getLength(); i++) {
Node element = elements.item(i);
if (element.getNodeType() != Node.ELEMENT_NODE) {
continue;
}
String elementName = element.getNodeName();
NamedNodeMap attributes = element.getAttributes();
if (ELEMENT_PROPERTY.equalsIgnoreCase(elementName)) {
// <property name="..." value="..."/>
// get the "name" attribute's value (required)
Node nameAtt = attributes.getNamedItem(PROPERTY_ATTRIBUTE_NAME);
if (nameAtt == null) {
throw new JDOFatalUserException(MSG.msg("EXC_PropertyElementHasNoNameAttribute", url));
}
String name = nameAtt.getNodeValue().trim();
if ("".equals(name)) {
throw new JDOFatalUserException(
MSG.msg("EXC_PropertyElementNameAttributeHasNoValue", name, url));
}
// The next call allows users to use either the
// <persistence-manager-factory> attribute names or the
// "javax.jdo" property names in <property> element "name"
// attributes. Handy-dandy.
String jdoPropertyName = ATTRIBUTE_PROPERTY_XREF.get(name);
String propertyName = jdoPropertyName != null ? jdoPropertyName : name;
if (p.containsKey(propertyName)) {
throw new JDOFatalUserException(
MSG.msg("EXC_DuplicatePropertyNameGivenInPropertyElement", propertyName, url));
}
// get the "value" attribute's value (optional)
Node valueAtt = attributes.getNamedItem(PROPERTY_ATTRIBUTE_VALUE);
String value = valueAtt == null ? null : valueAtt.getNodeValue().trim();
p.put(propertyName, value);
} else if (ELEMENT_INSTANCE_LIFECYCLE_LISTENER.equals(elementName)) {
// <instance-lifecycle-listener listener="..." classes="..."/>
// get the "listener" attribute's value
Node listenerAtt = attributes.getNamedItem(INSTANCE_LIFECYCLE_LISTENER_ATTRIBUTE_LISTENER);
if (listenerAtt == null) {
throw new JDOFatalUserException(MSG.msg("EXC_MissingListenerAttribute", url));
}
String listener = listenerAtt.getNodeValue().trim();
if ("".equals(listener)) {
throw new JDOFatalUserException(MSG.msg("EXC_MissingListenerAttributeValue", url));
}
// listener properties are of the form
// "javax.jdo.option.InstanceLifecycleListener." + listener
listener = PROPERTY_PREFIX_INSTANCE_LIFECYCLE_LISTENER + listener;
// get the "classes" attribute's value (optional)
Node classesAtt = attributes.getNamedItem(INSTANCE_LIFECYCLE_LISTENER_ATTRIBUTE_CLASSES);
String value = classesAtt == null ? "" : classesAtt.getNodeValue().trim();
p.put(listener, value);
}
}
return p;
}
protected static boolean isNullOrBlank(String s) {
return s == null || "".equals(s.trim());
}
/**
* Returns a {@link PersistenceManagerFactory} configured based on the properties stored in the
* file at <code>propsFile</code>. This method is equivalent to invoking {@link
* #getPersistenceManagerFactory(File,ClassLoader)} with <code>
* Thread.currentThread().getContextClassLoader()</code> as the <code>loader</code> argument.
*
* @since 2.0
* @param propsFile the file containing the Properties
* @return the PersistenceManagerFactory
*/
public static PersistenceManagerFactory getPersistenceManagerFactory(File propsFile) {
return getPersistenceManagerFactory(propsFile, getContextClassLoader());
}
/**
* Returns a {@link PersistenceManagerFactory} configured based on the properties stored in the
* file at <code>propsFile</code>. Creates a {@link PersistenceManagerFactory} with <code>loader
* </code>. Any <code>IOException</code>s or <code>FileNotFoundException</code>s thrown during
* resource loading will be wrapped in a {@link JDOFatalUserException}.
*
* @since 2.0
* @param propsFile the file containing the Properties
* @param loader the class loader to use to load the <code>PersistenceManagerFactory</code> class
* @return the PersistenceManagerFactory
*/
public static PersistenceManagerFactory getPersistenceManagerFactory(
File propsFile, ClassLoader loader) {
if (propsFile == null) throw new JDOFatalUserException(MSG.msg("EXC_GetPMFNullFile")); // NOI18N
InputStream in = null;
try {
in = new FileInputStream(propsFile);
return getPersistenceManagerFactory(in, loader);
} catch (FileNotFoundException fnfe) {
throw new JDOFatalUserException(MSG.msg("EXC_GetPMFNoFile", propsFile), fnfe); // NOI18N
} finally {
if (in != null)
try {
in.close();
} catch (IOException ioe) {
}
}
}
/**
* Returns a {@link PersistenceManagerFactory} at the JNDI location specified by <code>
* jndiLocation</code> in the context <code>context</code>. If <code>context</code> is <code>null
* </code>, <code>new InitialContext()</code> will be used. This method is equivalent to invoking
* {@link #getPersistenceManagerFactory(String,Context,ClassLoader)} with <code>
* Thread.currentThread().getContextClassLoader()</code> as the <code>loader</code> argument.
*
* @since 2.0
* @param jndiLocation the JNDI location containing the PersistenceManagerFactory
* @param context the context in which to find the named PersistenceManagerFactory
* @return the PersistenceManagerFactory
*/
public static PersistenceManagerFactory getPersistenceManagerFactory(
String jndiLocation, Context context) {
return getPersistenceManagerFactory(jndiLocation, context, getContextClassLoader());
}
/**
* Returns a {@link PersistenceManagerFactory} at the JNDI location specified by <code>
* jndiLocation</code> in the context <code>context</code>. If <code>context</code> is <code>null
* </code>, <code>new InitialContext()</code> will be used. Creates a {@link
* PersistenceManagerFactory} with <code>loader</code>. Any <code>NamingException</code>s thrown
* will be wrapped in a {@link JDOFatalUserException}.
*
* @since 2.0
* @param jndiLocation the JNDI location containing the PersistenceManagerFactory
* @param context the context in which to find the named PersistenceManagerFactory
* @param loader the class loader to use to load the <code>PersistenceManagerFactory</code> class
* @return the PersistenceManagerFactory
*/
public static PersistenceManagerFactory getPersistenceManagerFactory(
String jndiLocation, Context context, ClassLoader loader) {
if (jndiLocation == null)
throw new JDOFatalUserException(MSG.msg("EXC_GetPMFNullJndiLoc")); // NOI18N
if (loader == null) throw new JDOFatalUserException(MSG.msg("EXC_GetPMFNullLoader")); // NOI18N
try {
if (context == null) context = new InitialContext();
Object o = context.lookup(jndiLocation);
return (PersistenceManagerFactory)
PortableRemoteObject.narrow(o, PersistenceManagerFactory.class);
} catch (NamingException ne) {
throw new JDOFatalUserException(
MSG.msg("EXC_GetPMFNamingException", jndiLocation, loader), ne); // NOI18N
}
}
/**
* Returns a {@link PersistenceManagerFactory} configured based on the Properties stored in the
* input stream at <code>stream</code>. This method is equivalent to invoking {@link
* #getPersistenceManagerFactory(InputStream,ClassLoader)} with <code>
* Thread.currentThread().getContextClassLoader()</code> as the <code>loader</code> argument.
*
* @since 2.0
* @param stream the stream containing the Properties
* @return the PersistenceManagerFactory
*/
public static PersistenceManagerFactory getPersistenceManagerFactory(InputStream stream) {
return getPersistenceManagerFactory(stream, getContextClassLoader());
}
/**
* Returns a {@link PersistenceManagerFactory} configured based on the Properties stored in the
* input stream at <code>stream</code>. Creates a {@link PersistenceManagerFactory} with <code>
* loader</code>. Any <code>IOException</code>s thrown during resource loading will be wrapped in
* a {@link JDOFatalUserException}.
*
* @since 2.0
* @param stream the stream containing the Properties
* @param loader the class loader to use to load the <code>PersistenceManagerFactory</code> class
* @return the PersistenceManagerFactory
*/
public static PersistenceManagerFactory getPersistenceManagerFactory(
InputStream stream, ClassLoader loader) {
if (stream == null) throw new JDOFatalUserException(MSG.msg("EXC_GetPMFNullStream")); // NOI18N
Properties props = new Properties();
try {
props.load(stream);
} catch (IOException ioe) {
throw new JDOFatalUserException(MSG.msg("EXC_GetPMFIOExceptionStream"), ioe); // NOI18N
}
return getPersistenceManagerFactory(props, loader);
}
/**
* Get a <code>JDOEnhancer</code> using the available enhancer(s) specified in
* "META-INF/services/JDOEnhancer" using the context class loader.
*
* @return the <code>JDOEnhancer</code>.
* @throws JDOFatalUserException if no available enhancer
* @since 3.0
*/
public static JDOEnhancer getEnhancer() {
return getEnhancer(getContextClassLoader());
}
/**
* Get a <code>JDOEnhancer</code> using the available enhancer(s) specified in
* "META-INF/services/JDOEnhancer"
*
* @param loader the loader to use for loading the JDOEnhancer class (if any)
* @return the <code>JDOEnhancer</code>.
* @throws JDOFatalUserException if no available enhancer
* @since 3.0
*/
public static JDOEnhancer getEnhancer(ClassLoader loader) {
ClassLoader ctrLoader = loader;
if (ctrLoader == null) {
ctrLoader = Thread.currentThread().getContextClassLoader();
}
/*
* If you have a jar file that provides the jdo enhancer implementation,
* a file naming the implementation goes into the file
* packaged into the jar file, called "META-INF/services/javax.jdo.JDOEnhancer".
* The contents of the file is a string that is the enhancer class name.
* For each file in the class loader named "META-INF/services/javax.jdo.JDOEnhancer",
* this method will invoke the default constructor of the implementation class.
* Return the enhancer if a valid class name is extracted from resources and
* the invocation returns an instance.
* Otherwise add the exception thrown to an exception list.
*/
ArrayList<Throwable> exceptions = new ArrayList<>();
int numberOfJDOEnhancers = 0;
try {
Enumeration<URL> urls = getResources(loader, SERVICE_LOOKUP_ENHANCER_RESOURCE_NAME);
if (urls != null) {
while (urls.hasMoreElements()) {
numberOfJDOEnhancers++;
try {
String enhancerClassName = getClassNameFromURL(urls.nextElement());
Class<?> enhancerClass = forName(enhancerClassName, true, ctrLoader);
JDOEnhancer enhancer = (JDOEnhancer) enhancerClass.newInstance();
return enhancer;
} catch (Throwable ex) {
// remember exceptions from failed enhancer invocations
exceptions.add(ex);
}
}
}
} catch (Throwable ex) {
exceptions.add(ex);
}
throw new JDOFatalUserException(
MSG.msg("EXC_GetEnhancerNoValidEnhancerAvailable", numberOfJDOEnhancers),
exceptions.toArray(new Throwable[exceptions.size()]));
}
/**
* Get the context class loader associated with the current thread. This is done in a doPrivileged
* block because it is a secure method.
*
* @return the current thread's context class loader.
* @since 2.0
*/
private static ClassLoader getContextClassLoader() {
return doPrivileged(
(PrivilegedAction<ClassLoader>) () -> Thread.currentThread().getContextClassLoader());
}
/**
* Get the named resource as a stream from the resource loader. Perform this operation in a
* doPrivileged block.
*/
private static InputStream getResourceAsStream(
final ClassLoader resourceLoader, final String name) {
return doPrivileged(
(PrivilegedAction<InputStream>) () -> resourceLoader.getResourceAsStream(name));
}
/**
* Get the named Method from the named class. Perform this operation in a doPrivileged block.
*
* @param implClass the class
* @param methodName the name of the method
* @param parameterTypes the parameter types of the method
* @return the Method instance
*/
private static Method getMethod(
final Class<?> implClass, final String methodName, final Class<?>[] parameterTypes)
throws NoSuchMethodException {
try {
return doPrivileged(
(PrivilegedExceptionAction<Method>)
() -> implClass.getMethod(methodName, parameterTypes));
} catch (PrivilegedActionException ex) {
throw (NoSuchMethodException) ex.getException();
}
}
/** Invoke the method. Perform this operation in a doPrivileged block. */
private static Object invoke(
final Method method, final Object instance, final Object[] parameters)
throws IllegalAccessException, InvocationTargetException {
try {
return doPrivileged(
(PrivilegedExceptionAction<Object>) () -> method.invoke(instance, parameters));
} catch (PrivilegedActionException ex) {
Exception cause = ex.getException();
if (cause instanceof IllegalAccessException) throw (IllegalAccessException) cause;
else // if (cause instanceof InvocationTargetException)
throw (InvocationTargetException) cause;
}
}
/**
* Get resources of the resource loader. Perform this operation in a doPrivileged block.
*
* @param resourceLoader ClassLoader to use for loading resources
* @param resourceName Name of the resource
* @return the resources
* @throws IOException if an error occurs accessing the resources
*/
protected static Enumeration<URL> getResources(
final ClassLoader resourceLoader, final String resourceName) throws IOException {
try {
return doPrivileged(
(PrivilegedExceptionAction<Enumeration<URL>>)
() -> resourceLoader.getResources(resourceName));
} catch (PrivilegedActionException ex) {
throw (IOException) ex.getException();
}
}
/**
* Get the named class. Perform this operation in a doPrivileged block.
*
* @param name the name of the class
* @param init whether to initialize the class
* @param loader which class loader to use
* @return the class
*/
private static Class<?> forName(final String name, final boolean init, final ClassLoader loader)
throws ClassNotFoundException {
try {
return doPrivileged(
(PrivilegedExceptionAction<Class<?>>) () -> Class.forName(name, init, loader));
} catch (PrivilegedActionException ex) {
throw (ClassNotFoundException) ex.getException();
}
}
/**
* Open an input stream on the url. Perform this operation in a doPrivileged block.
*
* @param url
* @return the input stream
*/
private static InputStream openStream(final URL url) throws IOException {
try {
return doPrivileged((PrivilegedExceptionAction<InputStream>) () -> url.openStream());
} catch (PrivilegedActionException ex) {
throw (IOException) ex.getException();
}
}
@SuppressWarnings("unchecked")
private static <T> T doPrivileged(PrivilegedAction<T> privilegedAction) {
try {
return (T) LegacyJava.doPrivilegedAction.invoke(null, privilegedAction);
} catch (IllegalAccessException | InvocationTargetException e) {
if (e.getCause() instanceof RuntimeException) {
throw (RuntimeException) e.getCause();
}
throw new JDOFatalInternalException(e.getMessage());
}
}
@SuppressWarnings("unchecked")
private static <T> T doPrivileged(PrivilegedExceptionAction<T> privilegedAction)
throws PrivilegedActionException {
try {
return (T) LegacyJava.doPrivilegedExceptionAction.invoke(null, privilegedAction);
} catch (IllegalAccessException | InvocationTargetException e) {
if (e.getCause() instanceof PrivilegedActionException) {
throw (PrivilegedActionException) e.getCause();
}
throw new PrivilegedActionException(e);
}
}
}