/**********************************************************************
Copyright (c) 2004 Erik Bengtson and others. All rights reserved.
Licensed 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.
Contributors:
2004 Andy Jefferson - added description of addition process.
2004 Andy Jefferson - added constructor, and try-catch on initialisation
2006 Andy Jefferson - renamed to PersistenceConfiguration so that it is API agnostic
2008 Andy Jefferson - rewritten to have properties map and not need Java beans setters/getters
...
**********************************************************************/
package org.jpox;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TimeZone;
import org.jpox.exceptions.JPOXUserException;
import org.jpox.plugin.ConfigurationElement;
import org.jpox.plugin.PluginManager;
import org.jpox.properties.PersistencePropertyValidator;
import org.jpox.properties.PropertyTypeInvalidException;
import org.jpox.util.JPOXLogger;
import org.jpox.util.Localiser;
/**
* Class providing configuration for persistence.
* Persistence properties should be defined in the "plugin.xml" of the plugin that originates them. This class
* will read the available defined plugin properties (and their defaults, validators etc) and loaded up
* accordingly. Then any user-provided properties are superimposed over the top of these. Components can
* then access these properties using any of the convenience accessors for boolean, Boolean, long, int, Object,
* String types.
*/
public abstract class PersistenceConfiguration
{
/** Localisation of messages. */
protected static final Localiser LOCALISER = Localiser.getInstance("org.jpox.Localisation",
ObjectManager.class.getClassLoader());
/** Map of persistence properties. */
private Map properties = new HashMap();
/** Mapping for the properties of the plugins, PropertyMapping, keyed by the property name. */
private Map propertyMappings = new HashMap();
/** Calendar for this datastore. */
private Calendar dateTimezoneCalendar = null;
/** Flag defining if this is running within the JCA adaptor. */
private Boolean jca = null;
/** Flag for whether this object is still configurable. */
protected transient boolean configurable = true;
/**
* Convenience class wrapping the plugin property specification information.
*/
class PropertyMapping
{
String name;
String internalName;
String value;
String validatorName;
public PropertyMapping(String name, String intName, String val, String validator)
{
this.name = name;
this.internalName = intName;
this.value = val;
this.validatorName = validator;
}
}
/**
* Constructor.
*/
public PersistenceConfiguration()
{
}
/**
* Accessor for the options for this persistence configuration
* @return the options for this persistence configuration
*/
public Map getOptions()
{
return properties;
}
/**
* Method to set the persistence property defaults based on what is defined for plugins.
* @param pluginMgr The plugin manager
*/
public void setDefaultProperties(PluginManager pluginMgr)
{
ConfigurationElement[] propElements =
pluginMgr.getConfigurationElementsForExtension("org.jpox.persistence_properties", null, null);
if (propElements != null)
{
for (int i=0;i<propElements.length;i++)
{
String name = propElements[i].getAttribute("name");
String intName = propElements[i].getAttribute("internal-name");
String value = propElements[i].getAttribute("value");
propertyMappings.put(name, new PropertyMapping(name, intName, value,
propElements[i].getAttribute("validator")));
Object systemValue = System.getProperty(name);
if (systemValue != null)
{
// System value provided so add property with that value
if (intName != null)
{
properties.put(intName, systemValue);
}
else
{
properties.put(name, systemValue);
}
}
else
{
if (!properties.containsKey(name) && value != null)
{
// Property has default value and not yet set so add property with that value
if (intName != null)
{
properties.put(intName, value);
}
else
{
properties.put(name, value);
}
}
}
}
}
}
/**
* Accessor for whether a particular property is defined.
* @param name Property name
* @return Whether the property is defined
*/
public boolean hasProperty(String name)
{
return properties.get(name) != null;
}
/**
* Accessor for the specified property as a long.
* If the specified property isn't found returns 0.
* @param name Name of the property
* @return Long value for the property
* @throws PropertyTypeInvalidException thrown when the property is not available as this type
*/
public long getLongProperty(String name)
{
Object obj = properties.get(name);
if (obj != null)
{
if (obj instanceof Number)
{
return ((Number)obj).longValue();
}
else if (obj instanceof String)
{
Long longVal = new Long((String)obj);
properties.put(name, longVal); // Replace String value with Long
return longVal.longValue();
}
}
else
{
return 0;
}
throw new PropertyTypeInvalidException(name, "long");
}
/**
* Accessor for the specified property as an int.
* If the specified property isn't found returns 0.
* @param name Name of the property
* @return Int value for the property
* @throws PropertyTypeInvalidException thrown when the property is not available as this type
*/
public int getIntProperty(String name)
{
Object obj = properties.get(name);
if (obj != null)
{
if (obj instanceof Number)
{
return ((Number)obj).intValue();
}
else if (obj instanceof String)
{
Integer intVal = new Integer((String)obj);
properties.put(name, intVal); // Replace String value with Integer
return intVal.intValue();
}
}
else
{
return 0;
}
throw new PropertyTypeInvalidException(name, "int");
}
/**
* Accessor for the specified property as a boolean.
* If the specified property isn't found returns false.
* @param name Name of the property
* @return Boolean value for the property
* @throws PropertyTypeInvalidException thrown when the property is not available as this type
*/
public boolean getBooleanProperty(String name)
{
Object obj = properties.get(name);
if (obj != null)
{
if (obj instanceof Boolean)
{
return ((Boolean)obj).booleanValue();
}
else if (obj instanceof String)
{
Boolean boolVal = new Boolean((String)obj);
properties.put(name, boolVal); // Replace String value with Boolean
return boolVal.booleanValue();
}
}
else
{
return false;
}
throw new PropertyTypeInvalidException(name, "boolean");
}
/**
* Accessor for the specified property as a Boolean.
* If the specified property isn't found returns false.
* @param name Name of the property
* @return Boolean value for the property
* @throws PropertyTypeInvalidException thrown when the property is not available as this type
*/
public Boolean getBooleanObjectProperty(String name)
{
Object obj = properties.get(name);
if (obj != null)
{
if (obj instanceof Boolean)
{
return ((Boolean)obj);
}
else if (obj instanceof String)
{
Boolean boolVal = new Boolean((String)obj);
properties.put(name, boolVal); // Replace String value with Boolean
return boolVal;
}
}
else
{
return null;
}
throw new PropertyTypeInvalidException(name, "Boolean");
}
/**
* Accessor for the specified property as a String.
* If the specified property isn't found returns null.
* @param name Name of the property
* @return String value for the property
* @throws PropertyTypeInvalidException thrown when the property is not available as this type
*/
public String getStringProperty(String name)
{
Object obj = properties.get(name);
if (obj != null)
{
if (obj instanceof String)
{
return ((String)obj);
}
}
else
{
return null;
}
throw new PropertyTypeInvalidException(name, "String");
}
/**
* Accessor for the specified property as an Object.
* If the specified property isn't found returns null.
* @param name Name of the property
* @return Value for the property
*/
public Object getProperty(String name)
{
return properties.get(name);
}
/**
* Equality operator.
* @param obj Object to compare against.
* @return Whether the objects are equal.
**/
public synchronized boolean equals(Object obj)
{
if (obj == this)
{
return true;
}
if (!(obj instanceof PersistenceConfiguration))
{
return false;
}
PersistenceConfiguration config = (PersistenceConfiguration)obj;
return equalsVariable(properties, config.properties);
}
/**
* Equals two variables testing for nulls
* @param var1 the first variable
* @param var2 the second variable
* @return true if equals
*/
private boolean equalsVariable(Object var1, Object var2)
{
if (var1 == null)
{
if (var2 != null)
{
return false;
}
}
else if (!var1.equals(var2))
{
return false;
}
return true;
}
/**
* Accessor for the Calendar to be used in handling all timezone issues with the datastore.
* Utilises the "serverTimeZoneID" in providing this Calendar used in time/date conversions.
* @return The calendar to use for dateTimezone issues.
*/
public Calendar getCalendarForDateTimezone()
{
if (dateTimezoneCalendar == null)
{
TimeZone tz;
String serverTimeZoneID = getStringProperty("org.jpox.ServerTimeZoneID");
if (serverTimeZoneID != null)
{
tz = TimeZone.getTimeZone(serverTimeZoneID);
}
else
{
tz = TimeZone.getDefault();
}
dateTimezoneCalendar = new GregorianCalendar(tz);
}
return dateTimezoneCalendar;
}
/**
* Mutator to set the primary class loader.
* @param loader Loader
*/
public void setPrimaryClassLoader(ClassLoader loader)
{
setProperty("org.jpox.primaryClassLoader", loader);
}
/**
* Accessor for the JCA mode.
* @return true if using JCA connector.
*/
public ClassLoader getPrimaryClassLoader()
{
return (ClassLoader)getProperty("org.jpox.primaryClassLoader");
}
/**
* Mutator for the JCA mode.
* @param jca true if using JCA connector
*/
public synchronized void setJCAMode(Boolean jca)
{
assertConfigurable();
this.jca = jca;
}
/**
* Accessor for the JCA mode.
* @return true if using JCA connector.
*/
public boolean isJcaMode()
{
return jca != null && jca.booleanValue();
}
/**
* Accessor for the suffix for table identifiers
* @param value the suffix for table identifiers
*/
public synchronized void setPropertiesFile(String value)
{
assertConfigurable();
if (value == null)
{
return;
}
// try to load the properties file
Properties props = new Properties();
File file = new File(value);
if (file.exists())
{
properties.put("org.jpox.propertiesFile", value);
try
{
InputStream is = new FileInputStream(file);
props.load(is);
is.close();
}
catch (FileNotFoundException e)
{
properties.remove("org.jpox.propertiesFile");
throw new JPOXUserException(LOCALISER.msg("008014", value), e).setFatal();
}
catch (IOException e)
{
properties.remove("org.jpox.propertiesFile");
throw new JPOXUserException(LOCALISER.msg("008014", value), e).setFatal();
}
}
else
{
// Try to load it as a resource in the CLASSPATH
try
{
InputStream is = PersistenceConfiguration.class.getClassLoader().getResourceAsStream(value);
props.load(is);
is.close();
properties.put("org.jpox.propertiesFile", value);
}
catch (Exception e)
{
// Still not loadable so throw exception
throw new JPOXUserException(LOCALISER.msg("008014", value), e).setFatal();
}
}
setOptions(props);
}
/**
* Set the options for this PersistenceManagerFactory based on the given map of properties.
* @param props The Properties to set the options from.
*/
public void setOptions(Map props)
{
Set keys = props.keySet();
Iterator keyIter = keys.iterator();
while (keyIter.hasNext())
{
Object keyObj = keyIter.next();
if (keyObj instanceof String)
{
String key = (String)keyObj;
Object valueObj = props.get(keyObj);
setProperty(key, valueObj);
}
}
}
/**
* Convenience method to set a persistence property.
* @param name Name of the property
* @param value Value
*/
public void setProperty(String name, Object value)
{
if (name != null)
{
PropertyMapping mapping = (PropertyMapping)propertyMappings.get(name);
if (mapping != null)
{
if (mapping.validatorName != null)
{
// TODO Use ClassLoaderResolver
PersistencePropertyValidator validator = null;
try
{
Class validatorCls = Class.forName(mapping.validatorName);
validator = (PersistencePropertyValidator)validatorCls.newInstance();
}
catch (Exception e)
{
JPOXLogger.JDO.warn("Error creating validator of type " + mapping.validatorName, e);
}
if (validator != null)
{
boolean validated = (mapping.internalName != null ?
validator.validate(mapping.internalName, value) :
validator.validate(name, value));
if (!validated)
{
throw new IllegalArgumentException(LOCALISER.msg("008012", name, value));
}
}
}
if (mapping.internalName != null)
{
properties.put(mapping.internalName, value);
}
else
{
properties.put(mapping.name, value);
}
// Special behaviour properties
if (name.equals("org.jpox.propertiesFile"))
{
// Load all properties from the specified file
setPropertiesFile((String)value);
}
else if (name.equals("org.jpox.messageCodesIncluded"))
{
// Set global log message code flag
boolean included = getBooleanProperty("org.jpox.messageCodesIncluded");
Localiser.setDisplayCodesInMessages(included);
}
}
else
{
// Unknown property so just add it.
properties.put(name, value);
}
}
}
/**
* Asserts that a change to a configuration property is allowed.
*/
protected void assertConfigurable()
{
if (!configurable)
{
throw new JPOXUserException(LOCALISER.msg("008016"));
}
}
}