javax.jdo.spi.JDOImplHelper Maven / Gradle / Ivy
Show all versions of jdo-api Show documentation
/*
* 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
*
* http://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
* limitations under the License.
*/
/*
* JDOImplHelper.java
*
*/
package javax.jdo.spi;
import org.xml.sax.ErrorHandler;
import java.lang.reflect.Constructor;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.text.DateFormat;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Currency;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import javax.jdo.Constants;
import javax.jdo.JDOException;
import javax.jdo.JDOFatalInternalException;
import javax.jdo.JDOFatalUserException;
import javax.jdo.JDOHelper;
import javax.jdo.JDOUserException;
import javax.xml.parsers.DocumentBuilderFactory;
/** This class is a helper class for JDO implementations. It contains methods
* to register metadata for persistence-capable classes and to perform common
* operations needed by implementations, not by end users.
* JDOImplHelper
allows construction of instances of
* persistence-capable classes without using reflection.
*
Persistence-capable classes register themselves via a static method
* at class load time.
* There is no security restriction on this access. JDO implementations
* get access to the functions provided by this class only if they are
* authorized by the security manager. To avoid having every call go through
* the security manager, only the call to get an instance is checked. Once an
* implementation
* has an instance, any of the methods can be invoked without security checks.
* @version 2.1
*
*/
public class JDOImplHelper extends java.lang.Object {
/** This synchronized HashMap
contains a static mapping of
* PersistenceCapable
class to
* metadata for the class used for constructing new instances. New entries
* are added by the static method in each PersistenceCapable
* class. Entries are never removed.
*/
private static Map registeredClasses =
Collections.synchronizedMap(new HashMap ());
/** This Set contains all classes that have registered for setStateManager
* permissions via authorizeStateManagerClass.
* Only the key is used in order to maintain a weak set of classes.
*/
private static final Map
authorizedStateManagerClasses = new WeakHashMap();
/** This list contains the registered listeners for
* RegisterClassEvent
s.
*/
private static final List
listeners = new ArrayList();
/** The list of registered StateInterrogation instances
*/
private static List
stateInterrogations = new ArrayList();
/** The singleton JDOImplHelper
instance.
*/
private static JDOImplHelper jdoImplHelper = new JDOImplHelper();
/** The Internationalization message helper.
*/
private final static I18NHelper msg =
I18NHelper.getInstance ("javax.jdo.Bundle"); //NOI18N
/** The DateFormat pattern.
*/
private static String dateFormatPattern;
/** The default DateFormat instance.
*/
private static DateFormat dateFormat;
/**
* The DocumentBuilderFactory used during jdoconfig.xml parsing.
*/
private static DocumentBuilderFactory documentBuilderFactory;
/**
* The ErrorHandler used during jdoconfig.xml parsing.
*/
private static ErrorHandler errorHandler;
/**
* JDO standard properties that the user can configure.
*/
public static final Set USER_CONFIGURABLE_STANDARD_PROPERTIES = createUserConfigurableStandardProperties();
private static Set createUserConfigurableStandardProperties() {
Set props = new HashSet();
props.add(Constants.PROPERTY_CONNECTION_DRIVER_NAME);
props.add(Constants.PROPERTY_CONNECTION_FACTORY2_NAME);
props.add(Constants.PROPERTY_CONNECTION_FACTORY_NAME);
props.add(Constants.PROPERTY_CONNECTION_PASSWORD);
props.add(Constants.PROPERTY_CONNECTION_URL);
props.add(Constants.PROPERTY_CONNECTION_USER_NAME);
props.add(Constants.PROPERTY_COPY_ON_ATTACH);
props.add(Constants.PROPERTY_DATASTORE_READ_TIMEOUT_MILLIS);
props.add(Constants.PROPERTY_DATASTORE_WRITE_TIMEOUT_MILLIS);
props.add(Constants.PROPERTY_DETACH_ALL_ON_COMMIT);
props.add(Constants.PROPERTY_IGNORE_CACHE);
props.add(Constants.PROPERTY_INSTANCE_LIFECYCLE_LISTENER);
props.add(Constants.PROPERTY_MAPPING);
props.add(Constants.PROPERTY_MAPPING_CATALOG);
props.add(Constants.PROPERTY_MAPPING_SCHEMA);
props.add(Constants.PROPERTY_MULTITHREADED);
props.add(Constants.PROPERTY_NAME);
props.add(Constants.PROPERTY_NONTRANSACTIONAL_READ);
props.add(Constants.PROPERTY_NONTRANSACTIONAL_WRITE);
props.add(Constants.PROPERTY_OPTIMISTIC);
props.add(Constants.PROPERTY_PERSISTENCE_MANAGER_FACTORY_CLASS);
props.add(Constants.PROPERTY_PERSISTENCE_UNIT_NAME);
props.add(Constants.PROPERTY_READONLY);
props.add(Constants.PROPERTY_RESTORE_VALUES);
props.add(Constants.PROPERTY_RETAIN_VALUES);
props.add(Constants.PROPERTY_SERVER_TIME_ZONE_ID);
props.add(Constants.PROPERTY_SPI_RESOURCE_NAME);
props.add(Constants.PROPERTY_TRANSACTION_ISOLATION_LEVEL);
props.add(Constants.PROPERTY_TRANSACTION_TYPE);
return Collections.unmodifiableSet(props);
}
static final String JAVAX_JDO_PREFIX_LOWER_CASED =
Constants.JAVAX_JDO_PREFIX.toLowerCase();
static final String PROPERTY_PREFIX_INSTANCE_LIFECYCLE_LISTENER_LOWER_CASED =
Constants.PROPERTY_PREFIX_INSTANCE_LIFECYCLE_LISTENER.toLowerCase();
static final Set USER_CONFIGURABLE_STANDARD_PROPERTIES_LOWER_CASED =
createUserConfigurableStandardPropertiesLowerCased();
static Set createUserConfigurableStandardPropertiesLowerCased() {
Set mixedCased = createUserConfigurableStandardProperties();
Set lowerCased =
new HashSet(mixedCased.size());
for (String propertyName : mixedCased) {
lowerCased.add(propertyName.toLowerCase());
}
return Collections.unmodifiableSet(lowerCased);
}
/** Register the default DateFormat instance.
*/
static {
jdoImplHelper.registerDateFormat(getDateTimeInstance());
}
/** Creates new JDOImplHelper */
private JDOImplHelper() {
}
/** Get an instance of JDOImplHelper
. This method
* checks that the caller is authorized for
* JDOPermission("getMetadata")
, and if not, throws
* SecurityException
.
* @return an instance of JDOImplHelper
.
* @throws SecurityException if the caller is not authorized for
* JDOPermission("getMetadata").
*/
public static JDOImplHelper getInstance()
throws SecurityException {
SecurityManager sec = System.getSecurityManager();
if (sec != null) {
// throws exception if caller is not authorized
sec.checkPermission (JDOPermission.GET_METADATA);
}
return jdoImplHelper;
}
/** Get the field names for a PersistenceCapable
class. The
* order of fields is the natural ordering of the String
class
* (without considering localization).
* @param pcClass the PersistenceCapable
class.
* @return the field names for the class.
*/
public String[] getFieldNames (Class pcClass) {
Meta meta = getMeta (pcClass);
return meta.getFieldNames();
}
/** Get the field types for a PersistenceCapable
class. The
* order of fields is the same as for field names.
* @param pcClass the PersistenceCapable
class.
* @return the field types for the class.
*/
public Class[] getFieldTypes (Class pcClass) {
Meta meta = getMeta (pcClass);
return meta.getFieldTypes();
}
/** Get the field flags for a PersistenceCapable
class. The
* order of fields is the same as for field names.
* @param pcClass the PersistenceCapable
class.
* @return the field types for the class.
*/
public byte[] getFieldFlags (Class pcClass) {
Meta meta = getMeta (pcClass);
return meta.getFieldFlags();
}
/** Get the persistence-capable superclass for a
* PersistenceCapable
class.
* @param pcClass the PersistenceCapable
class.
* @return The PersistenceCapable
superclass for this class,
* or null
if there isn't one.
*/
public Class getPersistenceCapableSuperclass (Class pcClass) {
Meta meta = getMeta (pcClass);
return meta.getPersistenceCapableSuperclass();
}
/** Create a new instance of the class and assign its
* jdoStateManager
. The new instance has its
* jdoFlags
set to LOAD_REQUIRED
.
* @see PersistenceCapable#jdoNewInstance(StateManager sm)
* @param pcClass the PersistenceCapable
class.
* @param sm the StateManager
which will own the new instance.
* @return the new instance, or null
if the class is not
* registered.
*/
public PersistenceCapable newInstance (Class pcClass, StateManager sm) {
Meta meta = getMeta (pcClass);
PersistenceCapable pcInstance = meta.getPC();
return pcInstance == null?null:pcInstance.jdoNewInstance(sm);
}
/** Create a new instance of the class and assign its
* jdoStateManager
and key values from the ObjectId. If the
* oid parameter is null
, no key values are copied.
* The new instance has its jdoFlags
set to
* LOAD_REQUIRED
.
* @see PersistenceCapable#jdoNewInstance(StateManager sm, Object oid)
* @param pcClass the PersistenceCapable
class.
* @param sm the StateManager
which will own the new instance.
* @return the new instance, or null
if the class is not
* registered.
* @param oid the ObjectId instance from which to copy key field values.
*/
public PersistenceCapable newInstance
(Class pcClass, StateManager sm, Object oid) {
Meta meta = getMeta (pcClass);
PersistenceCapable pcInstance = meta.getPC();
return pcInstance == null?null:pcInstance.jdoNewInstance(sm, oid);
}
/** Create a new instance of the ObjectId class of this
* PersistenceCapable
class.
* It is intended only for application identity. This method should
* not be called for classes that use single field identity;
* newObjectIdInstance(Class, Object) should be used instead.
* If the class has been
* enhanced for datastore identity, or if the class is abstract,
* null is returned.
* @param pcClass the PersistenceCapable
class.
* @return the new ObjectId instance, or null
if the class
* is not registered.
*/
public Object newObjectIdInstance (Class pcClass) {
Meta meta = getMeta (pcClass);
PersistenceCapable pcInstance = meta.getPC();
return pcInstance == null?null:pcInstance.jdoNewObjectIdInstance();
}
/** Create a new instance of the class used by the parameter Class
* for JDO identity, using the
* key constructor of the object id class. It is intended for single
* field identity. The identity
* instance returned has no relationship with the values of the primary key
* fields of the persistence-capable instance on which the method is called.
* If the key is the wrong class for the object id class, null is returned.
* For classes that use single field identity, if the parameter is
* of one of the following types, the behavior must be as specified:
*
Number
or Character
: the
* parameter must be the single field
* type or the wrapper class of the primitive field type; the parameter
* is passed to the single field identity constructor
* ObjectIdFieldSupplier
: the field value
* is fetched from the ObjectIdFieldSupplier
and passed to the
* single field identity constructor
* String
: the String is passed to the
* single field identity constructor
*
* @return the new ObjectId instance, or null
* if the class is not registered.
* @param obj the Object
form of the object id
* @param pcClass the PersistenceCapable
class.
* @since 2.0
*/
public Object newObjectIdInstance (Class pcClass, Object obj) {
Meta meta = getMeta (pcClass);
PersistenceCapable pcInstance = meta.getPC();
return (pcInstance == null)?null:pcInstance.jdoNewObjectIdInstance(obj);
}
/** Copy fields from an outside source to the key fields in the ObjectId.
* This method is generated in the PersistenceCapable
class to
* generate a call to the field manager for each key field in the ObjectId.
* For example, an ObjectId class that has three key fields
* (int id
, String name
, and
* Float salary
) would have the method generated:
*
* void jdoCopyKeyFieldsToObjectId (Object oid, ObjectIdFieldSupplier fm) {
*
oid.id = fm.fetchIntField (0);
*
oid.name = fm.fetchStringField (1);
*
oid.salary = fm.fetchObjectField (2);
*
}
*
The implementation is responsible for implementing the
* ObjectIdFieldSupplier
to provide the values for the key
* fields.
* @param pcClass the PersistenceCapable Class
.
* @param oid the ObjectId target of the copy.
* @param fm the field manager that supplies the field values.
*/
public void copyKeyFieldsToObjectId
(Class pcClass, PersistenceCapable.ObjectIdFieldSupplier fm, Object oid) {
Meta meta = getMeta (pcClass);
PersistenceCapable pcInstance = meta.getPC();
if (pcInstance == null) {
throw new JDOFatalInternalException (msg.msg(
"ERR_AbstractClassNoIdentity", pcClass.getName())); //NOI18N
}
pcInstance.jdoCopyKeyFieldsToObjectId(fm, oid);
}
/** Copy fields to an outside source from the key fields in the ObjectId.
* This method is generated in the PersistenceCapable
class to
* generate a call to the field manager for each key field in the ObjectId.
* For example, an ObjectId class that has three key fields
* (int id
, String name
, and
* Float salary
) would have the method generated:
*
void jdoCopyKeyFieldsFromObjectId
*
(PersistenceCapable oid, ObjectIdFieldConsumer fm) {
*
fm.storeIntField (0, oid.id);
*
fm.storeStringField (1, oid.name);
*
fm.storeObjectField (2, oid.salary);
*
}
*
The implementation is responsible for implementing the
* ObjectIdFieldConsumer
to store the values for the key
* fields.
* @param pcClass the PersistenceCapable
class
* @param oid the ObjectId source of the copy.
* @param fm the field manager that receives the field values.
*/
public void copyKeyFieldsFromObjectId
(Class pcClass, PersistenceCapable.ObjectIdFieldConsumer fm, Object oid) {
Meta meta = getMeta (pcClass);
PersistenceCapable pcInstance = meta.getPC();
if (pcInstance == null) {
throw new JDOFatalInternalException (msg.msg(
"ERR_AbstractClassNoIdentity", pcClass.getName())); //NOI18N
}
pcInstance.jdoCopyKeyFieldsFromObjectId(fm, oid);
}
/** Register metadata by class. The registration will be done in the
* class named JDOImplHelper
loaded by the same or an
* ancestor class loader as the PersistenceCapable
class
* performing the registration.
*
* @param pcClass the PersistenceCapable
class
* used as the key for lookup.
* @param fieldNames an array of String
field names for
* persistent and transactional fields
* @param fieldTypes an array of Class
field types
* @param fieldFlags the Field Flags for persistent and transactional fields
* @param pc an instance of the PersistenceCapable
class
* @param persistenceCapableSuperclass the most immediate superclass that is
* PersistenceCapable
*/
public static void registerClass (Class pcClass,
String[] fieldNames, Class[] fieldTypes,
byte[] fieldFlags, Class persistenceCapableSuperclass,
PersistenceCapable pc) {
if (pcClass == null)
throw new NullPointerException(msg.msg("ERR_NullClass")); //NOI18N
Meta meta = new Meta (fieldNames, fieldTypes,
fieldFlags, persistenceCapableSuperclass, pc);
registeredClasses.put (pcClass, meta);
// handle class registration listeners
synchronized (listeners) {
if (!listeners.isEmpty()) {
RegisterClassEvent event = new RegisterClassEvent(
jdoImplHelper, pcClass, fieldNames, fieldTypes,
fieldFlags, persistenceCapableSuperclass);
for (Iterator i = listeners.iterator(); i.hasNext();) {
RegisterClassListener crl =
(RegisterClassListener)i.next();
if (crl != null) {
crl.registerClass(event);
}
}
}
}
}
/**
* Unregister metadata by class loader. This method unregisters all
* registered PersistenceCapable
classes loaded by the
* specified class loader. Any attempt to get metadata for unregistered
* classes will result in a JDOFatalUserException
.
* @param cl the class loader.
* @since 1.0.2
*/
public void unregisterClasses (ClassLoader cl)
{
SecurityManager sec = System.getSecurityManager();
if (sec != null) {
// throws exception if caller is not authorized
sec.checkPermission (JDOPermission.MANAGE_METADATA);
}
synchronized(registeredClasses) {
for (Iterator i = registeredClasses.keySet().iterator();
i.hasNext();) {
Class pcClass = (Class)i.next();
// Note, the pc class was registered by calling the static
// method JDOImplHelper.registerClass. This means the
// JDOImplHelper class loader is the same as or an ancestor
// of the class loader of the pc class. In this case method
// getClassLoader does not perform a security check for
// RuntimePermission("getClassLoader") and thus we do not
// need a privileged block for the getClassLoader call.
if ((pcClass != null) && (pcClass.getClassLoader() == cl)) {
// unregister pc class, if its class loader is the
// specified one.
i.remove();
}
}
}
}
/**
* Unregister metadata by class. This method unregisters the specified
* class. Any further attempt to get metadata for the specified class will
* result in a JDOFatalUserException
.
* @param pcClass the PersistenceCapable
class to be
* unregistered.
* @since 1.0.2
*/
public void unregisterClass (Class pcClass)
{
if (pcClass == null)
throw new NullPointerException(msg.msg("ERR_NullClass")); //NOI18N
SecurityManager sec = System.getSecurityManager();
if (sec != null) {
// throws exception if caller is not authorized
sec.checkPermission (JDOPermission.MANAGE_METADATA);
}
registeredClasses.remove(pcClass);
}
/**
* Add the specified RegisterClassListener
to the listener
* list.
* @param crl the listener to be added
*/
public void addRegisterClassListener (RegisterClassListener crl) {
HashSet alreadyRegisteredClasses = null;
synchronized (listeners) {
listeners.add(crl);
// Make a copy of the existing set of registered classes.
// Between these two lines of code, any number of new class
// registrations might occur, and will then all wait until this
// synchronized block completes. Some of the class registrations
// might be delivered twice to the newly registered listener.
alreadyRegisteredClasses = new HashSet (registeredClasses.keySet());
}
// new registrations will call the new listener while the following
// occurs notify the new listener about already-registered classes
for (Iterator it = alreadyRegisteredClasses.iterator(); it.hasNext();) {
Class pcClass = (Class)it.next();
Meta meta = getMeta (pcClass);
RegisterClassEvent event = new RegisterClassEvent(
this, pcClass, meta.getFieldNames(), meta.getFieldTypes(),
meta.getFieldFlags(), meta.getPersistenceCapableSuperclass());
crl.registerClass (event);
}
}
/**
* Remove the specified RegisterClassListener
from the listener
* list.
* @param crl the listener to be removed
*/
public void removeRegisterClassListener (RegisterClassListener crl) {
synchronized (listeners) {
listeners.remove(crl);
}
}
/**
* Returns a collection of class objects of the registered
* persistence-capable classes.
* @return registered persistence-capable classes
*/
public Collection getRegisteredClasses() {
return Collections.unmodifiableCollection(registeredClasses.keySet());
}
/** Look up the metadata for a PersistenceCapable
class.
* @param pcClass the Class
.
* @return the Meta
for the Class
.
*/
private static Meta getMeta (Class pcClass) {
Meta ret = (Meta) registeredClasses.get (pcClass);
if (ret == null) {
throw new JDOFatalUserException(
msg.msg ("ERR_NoMetadata", pcClass.getName())); //NOI18N
}
return ret;
}
/** Register a class authorized to replaceStateManager. The caller of
* this method must be authorized for JDOPermission("setStateManager").
* During replaceStateManager, a persistence-capable class will call
* the corresponding checkAuthorizedStateManager and the class of the
* instance of the parameter must have been registered.
* @param smClass a Class that is authorized for
* JDOPermission("setStateManager").
* @throws SecurityException if the caller is not authorized for
* JDOPermission("setStateManager").
* @since 1.0.1
*/
public static void registerAuthorizedStateManagerClass (Class smClass)
throws SecurityException {
if (smClass == null)
throw new NullPointerException(msg.msg("ERR_NullClass")); //NOI18N
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(JDOPermission.SET_STATE_MANAGER);
}
synchronized (authorizedStateManagerClasses) {
authorizedStateManagerClasses.put(smClass, null);
}
}
/** Register classes authorized to replaceStateManager. The caller of
* this method must be authorized for JDOPermission("setStateManager").
* During replaceStateManager, a persistence-capable class will call
* the corresponding checkAuthorizedStateManager and the class of the
* instance of the parameter must have been registered.
* @param smClasses a Collection of Classes that are authorized for
* JDOPermission("setStateManager").
* @throws SecurityException if the caller is not authorized for
* JDOPermission("setStateManager").
* @since 1.0.1
*/
public static void registerAuthorizedStateManagerClasses (
Collection smClasses) throws SecurityException {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(JDOPermission.SET_STATE_MANAGER);
synchronized (authorizedStateManagerClasses) {
for (Iterator it = smClasses.iterator(); it.hasNext();) {
Object smClass = it.next();
if (!(smClass instanceof Class)) {
throw new ClassCastException(
msg.msg("ERR_StateManagerClassCast", //NOI18N
smClass.getClass().getName()));
}
registerAuthorizedStateManagerClass((Class)it.next());
}
}
}
}
/**
* Register a DocumentBuilderFactory instance for use in parsing the
* resource(s) META-INF/jdoconfig.xml. The default is governed by the
* semantics of DocumentBuilderFactory.newInstance().
*
* @param factory the DocumentBuilderFactory instance to use
* @since 2.1
*/
public synchronized void registerDocumentBuilderFactory(
DocumentBuilderFactory factory) {
documentBuilderFactory = factory;
}
/**
* Return the registered instance of DocumentBuilderFactory.
* @return the DocumentBuilderFactory if registered; null otherwise
* @since 2.1
*/
public static DocumentBuilderFactory getRegisteredDocumentBuilderFactory() {
return documentBuilderFactory;
}
/**
* Register an ErrorHandler instance for use in parsing the
* resource(s) META-INF/jdoconfig.xml. The default is an ErrorHandler
* that throws on error or fatalError and ignores warnings.
*
* @param handler the ErrorHandler instance to use
* @since 2.1
*/
public synchronized void registerErrorHandler(ErrorHandler handler) {
errorHandler = handler;
}
/**
* Return the registered instance of ErrorHandler.
* @return the registered ErrorHandler if registered; null otherwise
* @since 2.1
*/
public static ErrorHandler getRegisteredErrorHandler() {
return errorHandler;
}
/** Check that the parameter instance is of a class that is authorized for
* JDOPermission("setStateManager"). This method is called by the
* replaceStateManager method in persistence-capable classes.
* A class that is passed as the parameter to replaceStateManager must be
* authorized for JDOPermission("setStateManager"). To improve performance,
* first the set of authorized classes is checked, and if not present, a
* regular permission check is made. The regular permission check requires
* that all callers on the stack, including the persistence-capable class
* itself, must be authorized for JDOPermission("setStateManager").
* @param sm an instance of StateManager whose class is to be checked.
* @since 1.0.1
*/
public static void checkAuthorizedStateManager (StateManager sm) {
checkAuthorizedStateManagerClass(sm.getClass());
}
/** Check that the parameter instance is a class that is authorized for
* JDOPermission("setStateManager"). This method is called by the
* constructors of JDO Reference Implementation classes.
* @param smClass a Class to be checked for JDOPermission("setStateManager")
* @since 1.0.1
*/
public static void checkAuthorizedStateManagerClass (Class smClass) {
final SecurityManager scm = System.getSecurityManager();
if (scm == null) {
// if no security manager, no checking.
return;
}
synchronized(authorizedStateManagerClasses) {
if (authorizedStateManagerClasses.containsKey(smClass)) {
return;
}
}
// if not already authorized, perform "long" security checking.
scm.checkPermission(JDOPermission.SET_STATE_MANAGER);
}
/**
* Construct an instance of a key class using a String as input.
* This is a helper interface for use with ObjectIdentity.
* Classes without a String constructor (such as those in java.lang
* and java.util) will use this interface for constructing new instances.
* The result might be a singleton or use some other strategy.
*/
public interface StringConstructor {
/**
* Construct an instance of the class for which this instance
* is registered.
* @param s the parameter for construction
* @return the constructed object
*/
public Object construct(String s);
}
/**
* Special StringConstructor instances for use with specific
* classes that have no public String constructor. The Map is
* keyed on class instance and the value is an instance of
* StringConstructor.
*/
static final Map stringConstructorMap =
new HashMap();
/**
*
* Register special StringConstructor instances. These instances
* are for constructing instances from String parameters where there
* is no String constructor for them.
* @param cls the class to register a StringConstructor for
* @param sc the StringConstructor instance
* @return the previous StringConstructor registered for this class
*/
public Object registerStringConstructor(Class cls, StringConstructor sc) {
synchronized(stringConstructorMap) {
return stringConstructorMap.put(cls, sc);
}
}
/** Register the default special StringConstructor instances.
*/
static {
if (isClassLoadable("java.util.Currency")) {
jdoImplHelper.registerStringConstructor(
Currency.class, new StringConstructor() {
public Object construct(String s) {
try {
return Currency.getInstance(s);
} catch (IllegalArgumentException ex) {
throw new javax.jdo.JDOUserException(msg.msg(
"EXC_CurrencyStringConstructorIllegalArgument", //NOI18N
s), ex);
} catch (Exception ex) {
throw new JDOUserException(msg.msg(
"EXC_CurrencyStringConstructorException"), //NOI18N
ex);
}
}
});
}
jdoImplHelper.registerStringConstructor(Locale.class, new StringConstructor() {
public Object construct(String s) {
try {
return getLocale(s);
} catch (Exception ex) {
throw new JDOUserException(msg.msg(
"EXC_LocaleStringConstructorException"), ex); //NOI18N
}
}
});
jdoImplHelper.registerStringConstructor(Date.class, new StringConstructor() {
public synchronized Object construct(String s) {
try {
// first, try the String as a Long
return new Date(Long.parseLong(s));
} catch (NumberFormatException ex) {
// not a Long; try the formatted date
ParsePosition pp = new ParsePosition(0);
Date result = dateFormat.parse(s, pp);
if (result == null) {
throw new JDOUserException (msg.msg(
"EXC_DateStringConstructor", new Object[] //NOI18N
{s, new Integer(pp.getErrorIndex()),
dateFormatPattern}));
}
return result;
}
}
});
}
/**
* Parse the String to a Locale.
* @param s the name of the Locale
* @return the Locale corresponding to the name
*/
private static Locale getLocale(String s) {
String lang = s;
int firstUnderbar = s.indexOf('_');
if (firstUnderbar == -1) {
// nothing but language
return new Locale(lang);
}
lang = s.substring(0, firstUnderbar);
String country;
int secondUnderbar = s.indexOf('_', firstUnderbar + 1);
if (secondUnderbar == -1) {
// nothing but language, country
country = s.substring(firstUnderbar + 1);
return new Locale(lang, country);
}
country = s.substring(firstUnderbar + 1, secondUnderbar);
String variant = s.substring(secondUnderbar + 1);
return new Locale(lang, country, variant);
}
/**
* Determine if a class is loadable in the current environment.
* @param className the fully-qualified name of the class
* @return true if the class can be loaded; false otherwise
*/
private static boolean isClassLoadable(String className) {
try {
Class.forName(className);
return true;
} catch (ClassNotFoundException ex) {
return false;
}
}
/**
* Construct an instance of the parameter class, using the keyString
* as an argument to the constructor. If the class has a StringConstructor
* instance registered, use it. If not, try to find a constructor for
* the class with a single String argument. Otherwise, throw a
* JDOUserException.
* @param className the name of the class
* @param keyString the String parameter for the constructor
* @return the result of construction
*/
public static Object construct(String className, String keyString) {
StringConstructor stringConstructor;
try {
Class> keyClass = Class.forName(className);
synchronized(stringConstructorMap) {
stringConstructor =
(StringConstructor) stringConstructorMap.get(keyClass);
}
if (stringConstructor != null) {
return stringConstructor.construct(keyString);
} else {
Constructor keyConstructor =
keyClass.getConstructor(new Class[]{String.class});
return keyConstructor.newInstance(new Object[]{keyString});
}
} catch (JDOException ex) {
throw ex;
} catch (Exception ex) {
/* ClassNotFoundException,
NoSuchMethodException,
InstantiationException,
IllegalAccessException,
InvocationTargetException */
throw new JDOUserException(
msg.msg("EXC_ObjectIdentityStringConstruction", //NOI18N
new Object[] {ex.toString(), className, keyString}), ex);
}
}
/**
* Get the DateFormat instance for the default locale from the VM.
* This requires the following privileges for JDOImplHelper in the
* security permissions file:
* permission java.util.PropertyPermission "user.country", "read";
* permission java.util.PropertyPermission "user.timezone", "read,write";
* permission java.util.PropertyPermission "java.home", "read";
* If these permissions are not present, or there is some other
* problem getting the default date format, a simple formatter is returned.
* @since 2.1
* @return the default date-time formatter
*/
static DateFormat getDateTimeInstance() {
DateFormat result = null;
try {
result = AccessController.doPrivileged (
new PrivilegedAction () {
public DateFormat run () {
return DateFormat.getDateTimeInstance();
}
}
);
} catch (Exception ex) {
result = DateFormat.getInstance();
}
return result;
}
/**
* Register a DateFormat instance for use with constructing Date
* instances. The default is the default DateFormat instance.
* If the new instance implements SimpleDateFormat, get its pattern
* for error messages.
* @since 2.0
* @param df the DateFormat instance to use
*/
public synchronized void registerDateFormat(DateFormat df) {
dateFormat = df;
if (df instanceof SimpleDateFormat) {
dateFormatPattern = ((SimpleDateFormat)df).toPattern();
} else {
dateFormatPattern = msg.msg("MSG_unknown"); //NOI18N
}
}
/** This is a helper class to manage metadata per persistence-capable
* class. The information is used at runtime to provide field names and
* field types to the JDO Model.
*
* This is the value of the HashMap
which
* relates the PersistenceCapable Class
* as a key to the metadata.
*/
static class Meta {
/** Construct an instance of Meta
.
* @param fieldNames An array of String
* @param fieldTypes An array of Class
* @param fieldFlags an array of int
* @param persistenceCapableSuperclass the most immediate
* PersistenceCapable
superclass
* @param pc An instance of the PersistenceCapable
class
*/
Meta (String[] fieldNames, Class[] fieldTypes, byte[] fieldFlags,
Class persistenceCapableSuperclass, PersistenceCapable pc) {
this.fieldNames = fieldNames;
this.fieldTypes = fieldTypes;
this.fieldFlags = fieldFlags;
this.persistenceCapableSuperclass = persistenceCapableSuperclass;
this.pc = pc;
}
/** This is an array of field names used
* for the Model at runtime. The field
* is passed by the static class initialization.
*/
String[] fieldNames;
/** Get the field names from the metadata.
* @return the array of field names.
*/
String[] getFieldNames() {
return fieldNames;
}
/** This is an array of field types used
* for the Model at runtime. The field
* is passed by the static class initialization.
*/
Class[] fieldTypes;
/** Get the field types from the metadata.
* @return the array of field types.
*/
Class[] getFieldTypes() {
return fieldTypes;
}
/** This is an array of field flags used
* for the Model at runtime. The field
* is passed by the static class initialization.
*/
byte[] fieldFlags;
/** Get the field types from the metadata.
* @return the array of field types.
*/
byte[] getFieldFlags() {
return fieldFlags;
}
/** This is the Class
instance of the
* PersistenceCapable
superclass.
*/
Class persistenceCapableSuperclass;
/** Return the PersistenceCapable
superclass.
* @return the PersistenceCapable
superclass
*/
Class getPersistenceCapableSuperclass() {
return persistenceCapableSuperclass;
}
/** This is an instance of PersistenceCapable
,
* used at runtime to create new instances.
*/
PersistenceCapable pc;
/** Get an instance of the PersistenceCapable
class.
* @return an instance of the PersistenceCapable Class
.
*/
PersistenceCapable getPC() {
return pc;
}
/** Return the string form of the metadata.
* @return the string form
*/
public String toString() {
return "Meta-" + pc.getClass().getName(); //NOI18N
}
}
/**
* Add a StateInterrogation to the list. Create a new list
* in case there is an iterator open on the original list.
* @param si the StateInterrogation to add
*/
public synchronized void addStateInterrogation(StateInterrogation si) {
List newList =
new ArrayList(stateInterrogations);
newList.add(si);
stateInterrogations = newList;
}
/**
* Remove a StateInterrogation from the list. Create a new list
* in case there is an iterator open on the original list.
* @param si the StateInterrogation to remove
*/
public synchronized void removeStateInterrogation(StateInterrogation si) {
List newList =
new ArrayList(stateInterrogations);
newList.remove(si);
stateInterrogations = newList;
}
/**
* Return an Iterator over all StateInterrogation instances.
* Synchronize to avoid add/remove/iterate conflicts.
* @return an Iterator over all StateInterrogation instances.
*/
private synchronized Iterator getStateInterrogationIterator() {
return stateInterrogations.iterator();
}
/**
* Mark a non-binary-compatible instance dirty. Delegate to all
* registered StateInterrogation instances until one of them
* handles the call.
* @param pc the instance to mark dirty
* @param fieldName the field to mark dirty
*/
public void nonBinaryCompatibleMakeDirty(Object pc, String fieldName) {
Iterator sit = getStateInterrogationIterator();
while (sit.hasNext()) {
StateInterrogation si = (StateInterrogation)sit.next();
try {
if (si.makeDirty(pc, fieldName)) return;
} catch (Throwable t) {
continue; // ignore exceptions from errant StateInterrogations
}
}
}
/**
* Determine the state of a non-binary-compatible instance.
* Delegate to all registered StateInterrogation instances until
* one of them handles the call (returns a non-null Boolean
* with the answer).
* The caller provides the stateless "method object" that does
* the actual call to the StateInterrogation instance.
* @param pc the instance to be checked
* @param sibr the method object that delegates to the
* non-binary-compatible implementation
* @return Boolean.TRUE if the instance satisfies the state interrogation;
* Boolean.FALSE if the instance does not satisfy the interrogation;
* or null if the implementation does not manage the class of the instance
*/
public boolean nonBinaryCompatibleIs(Object pc,
StateInterrogationBooleanReturn sibr) {
Iterator sit = getStateInterrogationIterator();
while (sit.hasNext()) {
StateInterrogation si = (StateInterrogation)sit.next();
Boolean result;
try {
result = sibr.is(pc, si);
} catch (Throwable t) {
continue; // ignore exceptions from errant StateInterrogations
}
if (result != null) return result.booleanValue();
}
return false;
}
/**
* Return an object associated with a non-binary-compatible instance.
* Delegate to all registered StateInterrogation instances until
* one of them handles the call (returns a non-null answer).
* The caller provides the stateless "method object" that does
* the actual call to the StateInterrogation instance.
* @param pc the instance whose associated object is needed
* @param sibr the method object that delegates to the
* non-binary-compatible implementation
* @return the associated object or null if the implementation does not
* manage the class of the instance
*/
public Object nonBinaryCompatibleGet(Object pc,
StateInterrogationObjectReturn sibr) {
Iterator sit = getStateInterrogationIterator();
while (sit.hasNext()) {
StateInterrogation si = (StateInterrogation)sit.next();
Object result;
try {
result = sibr.get(pc, si);
} catch (Throwable t) {
continue; // ignore exceptions from errant StateInterrogations
}
if (result != null) return result;
}
return null;
}
/** This is an interface used to interrogate the state of an instance
* that does not implement PersistenceCapable. It is used for the
* methods that return a boolean value.
*/
public static interface StateInterrogationBooleanReturn {
/**
* Interrogate the state of the instance
* @param pc the instance
* @param si the method object
* @return the state of the instance or null
*/
public Boolean is(Object pc, StateInterrogation si);
}
/** This is an interface used to interrogate the state of an instance
* that does not implement PersistenceCapable. It is used for the
* methods that return an Object value.
*/
public static interface StateInterrogationObjectReturn {
/**
* Return the associated instance.
* @param pc the instance
* @param si the method object
* @return the associated object or null
*/
public Object get(Object pc, StateInterrogation si);
}
/**
* Examines the given map for keys beginning with the JDO standard prefix,
* {@link Constants#JAVAX_JDO_PREFIX}. If any property keys are found with
* that prefix but are unknown to this version of the JDO standard, a
* JDOUserException is thrown with a message indicating the unknown
* property. Keys that are not strings are ignored, as are string keys
* beginning with
* {@link Constants#PROPERTY_PREFIX_INSTANCE_LIFECYCLE_LISTENER} or not
* beginning with {@link Constants#JAVAX_JDO_PREFIX}.
*
* @param properties
* The properties to examine.
*
* @see Constants#JAVAX_JDO_PREFIX
* @see JDOHelper#USER_CONFIGURABLE_STANDARD_PROPERTIES
* @since 3.1
*/
public static void assertOnlyKnownStandardProperties(Map, ?> properties) {
if (properties == null || properties.isEmpty())
return;
List exceptions = new ArrayList();
StringBuilder unknowns = new StringBuilder();
for (Object key : properties.keySet()) {
if (!(key instanceof String)) {
continue; // ignore keys not of type string
}
String s = ((String) key).toLowerCase(); // compare case-insensitively
if (!s.startsWith(JAVAX_JDO_PREFIX_LOWER_CASED)) {
continue; // ignore vendor-specific keys
}
if (s.startsWith(PROPERTY_PREFIX_INSTANCE_LIFECYCLE_LISTENER_LOWER_CASED)) {
continue; // ignore listener class names
}
if (!USER_CONFIGURABLE_STANDARD_PROPERTIES_LOWER_CASED.contains(s)) {
exceptions.add(new JDOUserException(msg.msg(
"EXC_UnknownStandardProperty", s)));
if (exceptions.size() > 1) {
unknowns.append(",");
}
unknowns.append(s);
}
}
if (exceptions.size() == 1) {
throw exceptions.get(0);
} else if (exceptions.size() > 1) {
throw new JDOUserException(msg.msg("EXC_UnknownStandardProperties",
unknowns.toString()),
exceptions.toArray(new JDOUserException[] {}));
}
}
}