Home » apache-openjpa-1.1.0-source » org.apache.openjpa.lib » conf » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one
    3    * or more contributor license agreements.  See the NOTICE file
    4    * distributed with this work for additional information
    5    * regarding copyright ownership.  The ASF licenses this file
    6    * to you under the Apache License, Version 2.0 (the
    7    * "License"); you may not use this file except in compliance
    8    * with the License.  You may obtain a copy of the License at
    9    *
   10    * http://www.apache.org/licenses/LICENSE-2.0
   11    *
   12    * Unless required by applicable law or agreed to in writing,
   13    * software distributed under the License is distributed on an
   14    * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   15    * KIND, either express or implied.  See the License for the
   16    * specific language governing permissions and limitations
   17    * under the License.    
   18    */
   19   package org.apache.openjpa.lib.conf;
   20   
   21   import java.awt;
   22   import java.beans.BeanDescriptor;
   23   import java.beans.BeanInfo;
   24   import java.beans.EventSetDescriptor;
   25   import java.beans.IntrospectionException;
   26   import java.beans.Introspector;
   27   import java.beans.MethodDescriptor;
   28   import java.beans.PropertyChangeListener;
   29   import java.beans.PropertyChangeSupport;
   30   import java.beans.PropertyDescriptor;
   31   import java.io.Externalizable;
   32   import java.io.File;
   33   import java.io.IOException;
   34   import java.io.ObjectInput;
   35   import java.io.ObjectOutput;
   36   import java.io.PrintWriter;
   37   import java.io.Serializable;
   38   import java.io.StringWriter;
   39   import java.lang.reflect.Constructor;
   40   import java.lang.reflect.InvocationTargetException;
   41   import java.lang.reflect.Method;
   42   import java.security.AccessController;
   43   import java.util.ArrayList;
   44   import java.util.Arrays;
   45   import java.util.Collection;
   46   import java.util.Collections;
   47   import java.util.HashMap;
   48   import java.util.Iterator;
   49   import java.util.LinkedHashMap;
   50   import java.util.List;
   51   import java.util.Map;
   52   import java.util.MissingResourceException;
   53   import java.util.Properties;
   54   import java.util.TreeSet;
   55   
   56   import org.apache.commons.lang.StringUtils;
   57   import org.apache.openjpa.lib.log.Log;
   58   import org.apache.openjpa.lib.log.LogFactory;
   59   import org.apache.openjpa.lib.log.LogFactoryImpl;
   60   import org.apache.openjpa.lib.log.NoneLogFactory;
   61   import org.apache.openjpa.lib.util.Closeable;
   62   import org.apache.openjpa.lib.util.J2DoPrivHelper;
   63   import org.apache.openjpa.lib.util.Localizer;
   64   import org.apache.openjpa.lib.util.MultiClassLoader;
   65   import org.apache.openjpa.lib.util.ParseException;
   66   import org.apache.openjpa.lib.util.Services;
   67   import org.apache.openjpa.lib.util.StringDistance;
   68   import serp.util.Strings;
   69   
   70   /**
   71    * Default implementation of the {@link Configuration} interface.
   72    * Subclasses can choose to obtain configuration
   73    * information from JNDI, Properties, a Bean-builder, etc. This class
   74    * provides base configuration functionality, including serialization,
   75    * the <code>equals</code> and <code>hashCode</code> contracts, and default
   76    * property loading.
   77    * Property descriptors for {@link Value} instances are constructed from
   78    * the {@link Localizer} for the package of the configuration class. The
   79    * following localized strings will be used for describing a value, where
   80    * <em>name</em> is the last token of the value's property string:
   81    * <ul>
   82    * <li><em>name</em>-name: The name that will be displayed for the
   83    * option in a user interface; required.</li>
   84    * <li><em>name</em>-desc: A brief description of the option; required.</li>
   85    * <li><em>name</em>-type: The type or category name for this option;
   86    * required.</li>
   87    * <li><em>name</em>-expert: True if this is an expert option, false
   88    * otherwise; defaults to false.</li>
   89    * <li><em>name</em>-values: Set of expected or common values, excluding
   90    * alias keys; optional.</li>
   91    * <li><em>name</em>-interface: The class name of an interface whose
   92    * discoverable implementations should be included in the set of expected
   93    * or common values; optional.</li>
   94    * <li><em>name</em>-cat: The hierarchical category for the property
   95    * name, separated by ".".
   96    * <li><em>name</em>-displayorder: The order in which the property should
   97    * be displayer.</li>
   98    * </ul>
   99    *
  100    * @author Abe White
  101    */
  102   public class ConfigurationImpl
  103       implements Configuration, Externalizable, ValueListener {
  104   
  105       private static final String SEP = J2DoPrivHelper.getLineSeparator();
  106   
  107       private static final Localizer _loc = Localizer.forPackage
  108           (ConfigurationImpl.class);
  109   
  110       public ObjectValue logFactoryPlugin;
  111       public StringValue id;
  112   
  113       private String _product = null;
  114       private int _readOnlyState = INIT_STATE_LIQUID;
  115       private Map _props = null;
  116       private boolean _globals = false;
  117       private String _auto = null;
  118       private final List _vals = new ArrayList();
  119   
  120       // property listener helper
  121       private PropertyChangeSupport _changeSupport = null;
  122   
  123       // cache descriptors
  124       private PropertyDescriptor[] _pds = null;
  125       private MethodDescriptor[] _mds = null;
  126   
  127       /**
  128        * Default constructor. Attempts to load default properties through
  129        * system's configured {@link ProductDerivation}s.
  130        */
  131       public ConfigurationImpl() {
  132           this(true);
  133       }
  134   
  135       /**
  136        * Constructor.
  137        *
  138        * @param loadGlobals whether to attempt to load the global properties
  139        */
  140       public ConfigurationImpl(boolean loadGlobals) {
  141           setProductName("openjpa");
  142   
  143           logFactoryPlugin = addPlugin("Log", true);
  144           String[] aliases = new String[]{
  145               "true", LogFactoryImpl.class.getName(),
  146               "openjpa", LogFactoryImpl.class.getName(),
  147               "commons", "org.apache.openjpa.lib.log.CommonsLogFactory",
  148               "log4j", "org.apache.openjpa.lib.log.Log4JLogFactory",
  149               "none", NoneLogFactory.class.getName(),
  150               "false", NoneLogFactory.class.getName(),
  151           };
  152           logFactoryPlugin.setAliases(aliases);
  153           logFactoryPlugin.setDefault(aliases[0]);
  154           logFactoryPlugin.setString(aliases[0]);
  155           logFactoryPlugin.setInstantiatingGetter("getLogFactory");
  156   
  157           id = addString("Id");
  158           
  159           if (loadGlobals)
  160               loadGlobals();
  161       }
  162   
  163       /**
  164        * Automatically load global values from the system's
  165        * {@link ProductDerivation}s, and from System properties.
  166        */
  167       public boolean loadGlobals() {
  168           MultiClassLoader loader = (MultiClassLoader) AccessController
  169               .doPrivileged(J2DoPrivHelper.newMultiClassLoaderAction()); 
  170           loader.addClassLoader((ClassLoader) AccessController.doPrivileged(
  171               J2DoPrivHelper.getContextClassLoaderAction()));
  172           loader.addClassLoader(getClass().getClassLoader());
  173           ConfigurationProvider provider = ProductDerivations.loadGlobals(loader);
  174           if (provider != null)
  175               provider.setInto(this);
  176   
  177           // let system properties override other globals
  178           try {
  179               fromProperties(new HashMap(
  180                   (Properties) AccessController.doPrivileged(
  181                       J2DoPrivHelper.getPropertiesAction())));
  182           } catch (SecurityException se) {
  183               // security manager might disallow
  184           }
  185   
  186           _globals = true;
  187           if (provider == null) {
  188               Log log = getConfigurationLog();
  189               if (log.isTraceEnabled())
  190                   log.trace(_loc.get("no-default-providers"));
  191               return false;
  192           }
  193           return true;
  194       }
  195   
  196       public String getProductName() {
  197           return _product;
  198       }
  199   
  200       public void setProductName(String name) {
  201           _product = name;
  202       }
  203   
  204       public LogFactory getLogFactory() {
  205           if (logFactoryPlugin.get() == null)
  206               logFactoryPlugin.instantiate(LogFactory.class, this);
  207           return (LogFactory) logFactoryPlugin.get();
  208       }
  209   
  210       public void setLogFactory(LogFactory logFactory) {
  211           logFactoryPlugin.set(logFactory);
  212       }
  213   
  214       public String getLog() {
  215           return logFactoryPlugin.getString();
  216       }
  217   
  218       public void setLog(String log) {
  219           logFactoryPlugin.setString(log);
  220       }
  221   
  222       public Log getLog(String category) {
  223           return getLogFactory().getLog(category);
  224       }
  225   
  226       public String getId() {
  227           return id.get();
  228       }
  229       
  230       public void setId(String id) {
  231           this.id.set(id);
  232       }
  233   
  234       /**
  235        * Returns the logging channel <code>openjpa.Runtime</code> by default.
  236        */
  237       public Log getConfigurationLog() {
  238           return getLog("openjpa.Runtime");
  239       }
  240   
  241       public Value[] getValues() {
  242           return (Value[]) _vals.toArray(new Value[_vals.size()]);
  243       }
  244   
  245       public Value getValue(String property) {
  246           if (property == null)
  247               return null;
  248   
  249           // search backwards so that custom values added after construction
  250           // are found quickly, since this will be the std way of accessing them
  251           Value val;
  252           for (int i = _vals.size() - 1; i >= 0; i--) {
  253               val = (Value) _vals.get(i);
  254               if (val.getProperty().equals(property))
  255                   return val;
  256           }
  257           return null;
  258       }
  259   
  260       public void setReadOnly(int newState) {
  261           if (newState >= _readOnlyState) {
  262           	_readOnlyState = newState;
  263           }
  264       }
  265   
  266       public void instantiateAll() {
  267           StringWriter errs = null;
  268           PrintWriter stack = null;
  269           Value val;
  270           String getterName;
  271           Method getter;
  272           Object getterTarget;
  273           for (int i = 0; i < _vals.size(); i++) {
  274               val = (Value) _vals.get(i);
  275               getterName = val.getInstantiatingGetter();
  276               if (getterName == null)
  277                   continue;
  278   
  279               getterTarget = this;
  280               if (getterName.startsWith("this.")) {
  281                   getterName = getterName.substring("this.".length());
  282                   getterTarget = val;
  283               }
  284   
  285               try {
  286                   getter = getterTarget.getClass().getMethod(getterName,
  287                       (Class[]) null);
  288                   getter.invoke(getterTarget, (Object[]) null);
  289               } catch (Throwable t) {
  290                   if (t instanceof InvocationTargetException)
  291                       t = ((InvocationTargetException) t).getTargetException();
  292                   if (errs == null) {
  293                       errs = new StringWriter();
  294                       stack = new PrintWriter(errs);
  295                   } else
  296                       errs.write(SEP);
  297                   t.printStackTrace(stack);
  298                   stack.flush();
  299               }
  300           }
  301           if (errs != null)
  302               throw new RuntimeException(_loc.get("get-prop-errs",
  303                   errs.toString()).getMessage());
  304       }
  305   
  306       public boolean isReadOnly() {
  307           return _readOnlyState==INIT_STATE_FROZEN;
  308       }
  309   
  310       public void addPropertyChangeListener(PropertyChangeListener listener) {
  311           if (_changeSupport == null)
  312               _changeSupport = new PropertyChangeSupport(this);
  313           _changeSupport.addPropertyChangeListener(listener);
  314       }
  315   
  316       public void removePropertyChangeListener(PropertyChangeListener listener) {
  317           if (_changeSupport != null)
  318               _changeSupport.removePropertyChangeListener(listener);
  319       }
  320   
  321       public void valueChanged(Value val) {
  322           if (_changeSupport == null && _props == null)
  323               return;
  324   
  325           String newString = val.getString();
  326           if (_changeSupport != null)
  327               _changeSupport.firePropertyChange(val.getProperty(), null,
  328                   newString);
  329   
  330           // keep cached props up to date
  331           if (_props != null) {
  332               if (newString == null)
  333                   Configurations.removeProperty(val.getProperty(), _props);
  334               else if (Configurations.containsProperty(val.getProperty(), _props)
  335                   || val.getDefault() == null
  336                   || !val.getDefault().equals(newString))
  337                   put(_props, val, newString);
  338           }
  339       }
  340   
  341       /**
  342        * Closes all closeable values and plugins.
  343        */
  344       public final void close() {
  345           ProductDerivations.beforeClose(this);
  346           
  347           preClose();
  348           
  349           ObjectValue val;
  350           for (int i = 0; i < _vals.size(); i++) {
  351               if (_vals.get(i) instanceof Closeable) {
  352                   try { ((Closeable) _vals.get(i)).close(); }
  353                   catch (Exception e) {} 
  354                   continue;
  355               }
  356   
  357               if (!(_vals.get(i) instanceof ObjectValue))
  358                   continue;
  359   
  360               val = (ObjectValue) _vals.get(i);
  361               if (val.get() instanceof Closeable) {
  362                   try {
  363                       ((Closeable) val.get()).close();
  364                   } catch (Exception e) {
  365                   }
  366               }
  367           }
  368       }
  369       
  370       /**
  371        * Invoked by final method {@link #close} after invoking the 
  372        * {@link ProductDerivation#beforeConfigurationClose} callbacks
  373        * but before performing internal close operations.
  374        * 
  375        * @since 0.9.7
  376        */
  377       protected void preClose() {
  378       }
  379   
  380       ///////////////////////////
  381       // BeanInfo implementation
  382       ///////////////////////////
  383   
  384       public BeanInfo[] getAdditionalBeanInfo() {
  385           return new BeanInfo[0];
  386       }
  387   
  388       public BeanDescriptor getBeanDescriptor() {
  389           return new BeanDescriptor(getClass());
  390       }
  391   
  392       public int getDefaultEventIndex() {
  393           return 0;
  394       }
  395   
  396       public int getDefaultPropertyIndex() {
  397           return 0;
  398       }
  399   
  400       public EventSetDescriptor[] getEventSetDescriptors() {
  401           return new EventSetDescriptor[0];
  402       }
  403   
  404       public Image getIcon(int kind) {
  405           return null;
  406       }
  407   
  408       public synchronized MethodDescriptor[] getMethodDescriptors() {
  409           if (_mds != null)
  410               return _mds;
  411   
  412           PropertyDescriptor[] pds = getPropertyDescriptors();
  413           List descs = new ArrayList(); 
  414           for (int i = 0; i < pds.length; i++) {
  415               Method write = pds[i].getWriteMethod();
  416               Method read = pds[i].getReadMethod();
  417               if (read != null && write != null) {
  418                   descs.add(new MethodDescriptor(write));
  419                   descs.add(new MethodDescriptor(read));
  420               }
  421           }
  422           _mds = (MethodDescriptor[]) descs.
  423               toArray(new MethodDescriptor[descs.size()]);
  424           return _mds;
  425       }
  426   
  427       public synchronized PropertyDescriptor[] getPropertyDescriptors() {
  428           if (_pds != null)
  429               return _pds;
  430   
  431           _pds = new PropertyDescriptor[_vals.size()];
  432           List failures = null;
  433           Value val;
  434           for (int i = 0; i < _vals.size(); i++) {
  435               val = (Value) _vals.get(i);
  436               try {
  437                   _pds[i] = getPropertyDescriptor(val);
  438               } catch (MissingResourceException mre) {
  439                   if (failures == null)
  440                       failures = new ArrayList();
  441                   failures.add(val.getProperty());
  442               } catch (IntrospectionException ie) {
  443                   if (failures == null)
  444                       failures = new ArrayList();
  445                   failures.add(val.getProperty());
  446               }
  447           }
  448           if (failures != null)
  449               throw new ParseException(_loc.get("invalid-property-descriptors",
  450                   failures));
  451   
  452           return _pds;
  453       }
  454   
  455       /**
  456        * Create a property descriptor for the given value.
  457        */
  458       private PropertyDescriptor getPropertyDescriptor(Value val)
  459           throws IntrospectionException {
  460           String prop = val.getProperty();
  461           prop = prop.substring(prop.lastIndexOf('.') + 1);
  462   
  463           // set up property descriptor
  464           PropertyDescriptor pd;
  465           try {
  466               pd = new PropertyDescriptor(Introspector.decapitalize(prop), 
  467                   getClass());
  468           } catch (IntrospectionException ie) {
  469               // if there aren't any methods for this value(i.e., if it's a
  470               // dynamically-added value), then an IntrospectionException will
  471               // be thrown. Try to create a PD with no read or write methods.
  472               pd = new PropertyDescriptor(Introspector.decapitalize(prop),
  473                   (Method) null, (Method) null);
  474           }
  475           pd.setDisplayName(findLocalized(prop + "-name", true, val.getScope()));
  476           pd.setShortDescription(findLocalized(prop + "-desc", true,
  477               val.getScope()));
  478           pd.setExpert("true".equals(findLocalized(prop + "-expert", false,
  479               val.getScope())));
  480   
  481           try {
  482               pd.setReadMethod(getClass().getMethod("get"
  483                   + StringUtils.capitalize(prop), (Class[]) null));
  484               pd.setWriteMethod(getClass().getMethod("set"
  485                   + StringUtils.capitalize(prop), new Class[]
  486                   { pd.getReadMethod().getReturnType() }));
  487           } catch (Throwable t) {
  488               // if an error occurs, it might be because the value is a
  489               // dynamic property.
  490           }
  491   
  492           String type = findLocalized(prop + "-type", true, val.getScope());
  493           if (type != null)
  494               pd.setValue(ATTRIBUTE_TYPE, type);
  495   
  496           String cat = findLocalized(prop + "-cat", false, val.getScope());
  497           if (cat != null)
  498               pd.setValue(ATTRIBUTE_CATEGORY, cat);
  499           
  500           pd.setValue(ATTRIBUTE_XML, toXMLName(prop));
  501   
  502           String order = findLocalized(prop + "-displayorder", false,
  503               val.getScope());
  504           if (order != null)
  505               pd.setValue(ATTRIBUTE_ORDER, order);
  506   
  507           // collect allowed values from alias keys, listed values, and
  508           // interface implementors
  509           Collection allowed = new TreeSet();
  510           List aliases = Collections.EMPTY_LIST;
  511           if (val.getAliases() != null) {
  512               aliases = Arrays.asList(val.getAliases());
  513               for (int i = 0; i < aliases.size(); i += 2)
  514                   allowed.add(aliases.get(i));
  515           }
  516           String[] vals = Strings.split(findLocalized(prop
  517               + "-values", false, val.getScope()), ",", 0);
  518           for (int i = 0; i < vals.length; i++)
  519               if (!aliases.contains(vals[i]))
  520                   allowed.add(vals[i]);
  521           try {
  522               Class intf = Class.forName(findLocalized(prop
  523                   + "-interface", true, val.getScope()), false,
  524                   getClass().getClassLoader());
  525               pd.setValue(ATTRIBUTE_INTERFACE, intf.getName());
  526               String[] impls = Services.getImplementors(intf);
  527               for (int i = 0; i < impls.length; i++)
  528                   if (!aliases.contains(impls[i]))
  529                       allowed.add(impls[i]);
  530           } catch (Throwable t) {
  531           }
  532           if (!allowed.isEmpty())
  533               pd.setValue(ATTRIBUTE_ALLOWED_VALUES, (String[]) allowed.toArray
  534                   (new String[allowed.size()]));
  535   
  536           return pd;
  537       }
  538   
  539       /**
  540        * Find the given localized string, or return null if not found.
  541        */
  542       private String findLocalized(String key, boolean fatal, Class scope) {
  543           // find the localizer package that contains this key
  544           Localizer loc = null;
  545   
  546           // check the package that the value claims to be defined in, if
  547           // available, before we start guessing.
  548           if (scope != null) {
  549               loc = Localizer.forPackage(scope);
  550               try {
  551                   return loc.getFatal(key).getMessage();
  552               } catch (MissingResourceException mse) {
  553               }
  554           }
  555   
  556           for (Class cls = getClass(); cls != Object.class;
  557               cls = cls.getSuperclass()) {
  558               loc = Localizer.forPackage(cls);
  559               try {
  560                   return loc.getFatal(key).getMessage();
  561               } catch (MissingResourceException mse) {
  562               }
  563           }
  564   
  565           if (fatal)
  566               throw new MissingResourceException(key, getClass().getName(), key);
  567           return null;
  568       }
  569   
  570       ////////////////
  571       // To/from maps
  572       ////////////////
  573   
  574       public Map toProperties(boolean storeDefaults) {
  575           // clone properties before making any modifications; we need to keep
  576           // the internal properties instance consistent to maintain equals and
  577           // hashcode contracts
  578           Map clone;
  579           if (_props == null)
  580               clone = new HashMap();
  581           else if (_props instanceof Properties)
  582               clone = (Map) ((Properties) _props).clone();
  583           else
  584               clone = new HashMap(_props);
  585   
  586           // if no existing properties or the properties should contain entries
  587           // with default values, add values to properties
  588           if (_props == null || storeDefaults) {
  589               Value val;
  590               String str;
  591               for (int i = 0; i < _vals.size(); i++) {
  592                   // if key in existing properties, we already know value is up
  593                   // to date
  594                   val = (Value) _vals.get(i);
  595                   if (_props != null && Configurations.containsProperty
  596                       (val.getProperty(), _props))
  597                       continue;
  598   
  599                   str = val.getString();
  600                   if (str != null && (storeDefaults
  601                       || !str.equals(val.getDefault())))
  602                       put(clone, val, str);
  603               }
  604               if (_props == null)
  605                   _props = new HashMap(clone);
  606           }
  607           return clone;
  608       }
  609   
  610       public void fromProperties(Map map) {
  611           if (map == null || map.isEmpty())
  612               return;
  613           if (isReadOnly())
  614               throw new IllegalStateException(_loc.get("read-only").getMessage());
  615   
  616           // if the only previous call was to load defaults, forget them.
  617           // this way we preserve the original formatting of the user's props
  618           // instead of the defaults.  this is important for caching on
  619           // configuration objects
  620           if (_globals) {
  621               _props = null;
  622               _globals = false;
  623           }
  624   
  625           // copy the input to avoid mutation issues
  626           if (map instanceof HashMap)
  627               map = (Map) ((HashMap) map).clone();
  628           else if (map instanceof Properties)
  629               map = (Map) ((Properties) map).clone();
  630           else
  631               map = new LinkedHashMap(map);
  632   
  633           Map remaining = new HashMap(map);
  634           boolean ser = true;
  635           Value val;
  636           Object o;
  637           for (int i = 0; i < _vals.size(); i++) {
  638               val = (Value) _vals.get(i);
  639               o = get(map, val, true);
  640               if (o == null)
  641                   continue;
  642   
  643               if (o instanceof String) {
  644                   if (!StringUtils.equals((String) o, val.getString()))
  645                       val.setString((String) o);
  646               } else {
  647                   ser &= o instanceof Serializable;
  648                   val.setObject(o);
  649               }
  650               Configurations.removeProperty(val.getProperty(), remaining);
  651           }
  652           
  653           // convention is to point product at a resource with the
  654           // <prefix>.properties System property; remove that property so we
  655           // we don't warn about it
  656           Configurations.removeProperty("properties", remaining);
  657   
  658           // now warn if there are any remaining properties that there
  659           // is an unhandled prop, and remove the unknown properties
  660           Map.Entry entry;
  661           for (Iterator itr = remaining.entrySet().iterator(); itr.hasNext();) {
  662               entry = (Map.Entry) itr.next();
  663               Object key = entry.getKey();
  664               if (key != null) {
  665                   warnInvalidProperty((String) key);
  666                   map.remove(key);
  667               }
  668           }
  669   
  670           // cache properties
  671           if (_props == null && ser)
  672               _props = map;
  673       }
  674   
  675       /**
  676        * Adds <code>o</code> to <code>map</code> under key for <code>val</code>.
  677        * Use this method instead of attempting to add the value directly because 
  678        * this will account for the property prefix.
  679        */
  680       private void put(Map map, Value val, Object o) {
  681           Object key = val.getLoadKey();
  682           if (key == null)
  683               key = "openjpa." + val.getProperty();
  684           map.put(key, o);
  685       }
  686   
  687       /**
  688        * Look up the given value, testing all available prefixes.
  689        */
  690       private Object get(Map map, Value val, boolean setLoadKey) {
  691           String key = ProductDerivations.getConfigurationKey(
  692               val.getProperty(), map);
  693           if (map.containsKey(key) && setLoadKey)
  694               val.setLoadKey(key);
  695           return map.get(key);
  696       }
  697   
  698       /**
  699        * Issue a warning that the specified property is not valid.
  700        */
  701       private void warnInvalidProperty(String propName) {
  702           if (!isInvalidProperty(propName))
  703               return;
  704           Log log = getConfigurationLog();
  705           if (log == null || !log.isWarnEnabled())
  706               return;
  707   
  708           // try to find the closest string to the invalid property
  709           // so that we can provide a hint in case of a misspelling
  710           String closest = StringDistance.getClosestLevenshteinDistance
  711               (propName, newPropertyList(), 15);
  712   
  713           if (closest == null)
  714               log.warn(_loc.get("invalid-property", propName));
  715           else
  716               log.warn(_loc.get("invalid-property-hint", propName, closest));
  717       }
  718   
  719       /**
  720        * Return a comprehensive list of recognized map keys.
  721        */
  722       private Collection newPropertyList() {
  723           String[] prefixes = ProductDerivations.getConfigurationPrefixes();
  724           List l = new ArrayList(_vals.size() * prefixes.length);
  725           for (int i = 0; i < _vals.size(); i++) {
  726               for (int j = 0; j < prefixes.length; j++)
  727                   l.add(prefixes[j] + "." + ((Value) _vals.get(i)).getProperty());
  728           }
  729           return l;
  730       }
  731   
  732       /**
  733        * Returns true if the specified property name should raise a warning
  734        * if it is not found in the list of known properties.
  735        */
  736       protected boolean isInvalidProperty(String propName) {
  737           // handle warnings for openjpa.SomeString, but not for
  738           // openjpa.some.subpackage.SomeString, since it might be valid for some
  739           // specific implementation of OpenJPA
  740           String[] prefixes = ProductDerivations.getConfigurationPrefixes();
  741           for (int i = 0; i < prefixes.length; i++) {
  742               if (propName.toLowerCase().startsWith(prefixes[i])
  743                   && propName.length() > prefixes[i].length() + 1
  744                   && propName.indexOf('.', prefixes[i].length()) 
  745                   == prefixes[i].length()
  746                   && propName.indexOf('.', prefixes[i].length() + 1) == -1)
  747                   return true;
  748           }
  749           return false;
  750       }
  751   
  752       //////////////////////
  753       // Auto-configuration
  754       //////////////////////
  755   
  756       /**
  757        * This method loads the named resource as a properties file. It is
  758        * useful for auto-configuration tools so users can specify a
  759        * <code>properties</code> value with the name of a resource.
  760        */
  761       public void setProperties(String resourceName) throws IOException {
  762           ProductDerivations.load(resourceName, null, 
  763               getClass().getClassLoader()).setInto(this);
  764           _auto = resourceName;
  765       }
  766   
  767       /**
  768        * This method loads the named file as a properties file. It is
  769        * useful for auto-configuration tools so users can specify a
  770        * <code>propertiesFile</code> value with the name of a file.
  771        */
  772       public void setPropertiesFile(File file) throws IOException {
  773           ProductDerivations.load(file, null, getClass().getClassLoader()).
  774               setInto(this);
  775           _auto = file.toString();
  776       }
  777   
  778       /**
  779        * Return the resource that was set via auto-configuration methods
  780        * {@link #setProperties} or {@link #setPropertiesFile}, or null if none.
  781        */
  782       public String getPropertiesResource() {
  783           return _auto;
  784       }
  785   
  786       /////////////
  787       // Utilities
  788       /////////////
  789   
  790       /**
  791        * Performs an equality check based on equality of values.
  792        * {@link Value#equals(Object) Equality} of Values varies if the Value is
  793        * {@link Value#isDynamic() dynamic}.  
  794        */
  795       public boolean equals(Object other) {
  796           if (other == this)
  797               return true;
  798           if (other == null)
  799               return false;
  800           if (!getClass().equals(other.getClass()))
  801               return false;
  802   
  803           // compare properties
  804           ConfigurationImpl conf = (ConfigurationImpl) other;
  805           if (_vals.size() != conf.getValues().length)
  806           	return false;
  807           Iterator values = _vals.iterator();
  808           while (values.hasNext()) {
  809           	Value v = (Value)values.next();
  810           	Value thatV = conf.getValue(v.getProperty());
  811           	if (!v.equals(thatV)) {
  812           		return false;
  813           	}
  814           }
  815           return true;
  816       }
  817   
  818       /**
  819        * Computes hash code based on the hashCodes of the values.
  820        * {@link Value#hashCode() HashCode} of a Value varies if the Value is
  821        * {@link Value#isDynamic() dynamic}.  
  822        */
  823       public int hashCode() {
  824           Iterator values = _vals.iterator();
  825           int hash = 0;
  826           while (values.hasNext()) {
  827           	Value v = (Value)values.next();
  828           	hash += v.hashCode();
  829           }
  830           return hash;
  831       }
  832   
  833       /**
  834        * Convert <code>propName</code> to a lowercase-with-hyphens-style string.
  835        * This algorithm is only designed for mixes of uppercase and lowercase 
  836        * letters and lone digits. A more sophisticated conversion should probably 
  837        * be handled by a proper parser generator or regular expressions.
  838        */
  839       public static String toXMLName(String propName) {
  840           if (propName == null)
  841               return null;
  842           StringBuffer buf = new StringBuffer();
  843           char c;
  844           for (int i = 0; i < propName.length(); i++) {
  845               c = propName.charAt(i);
  846   
  847               // convert sequences of all-caps to downcase with dashes around 
  848               // them. put a trailing cap that is followed by downcase into the
  849               // downcase word.
  850               if (i != 0 && Character.isUpperCase(c) 
  851                   && (Character.isLowerCase(propName.charAt(i-1))
  852                   || (i > 1 && i < propName.length() - 1
  853                   && Character.isUpperCase(propName.charAt(i-1)) 
  854                   && Character.isLowerCase(propName.charAt(i+1)))))
  855                   buf.append('-');
  856               
  857               // surround sequences of digits with dashes.
  858               if (i != 0
  859                   && ((!Character.isLetter(c) && Character.isLetter(propName
  860                       .charAt(i - 1))) 
  861                   || (Character.isLetter(c) && !Character.isLetter(propName
  862                       .charAt(i - 1)))))
  863                   buf.append('-');
  864               
  865               buf.append(Character.toLowerCase(c));
  866           }
  867           return buf.toString();
  868       }
  869       
  870       /**
  871        * Implementation of the {@link Externalizable} interface to read from
  872        * the properties written by {@link #writeExternal}.
  873        */
  874       public void readExternal(ObjectInput in)
  875           throws IOException, ClassNotFoundException {
  876           fromProperties((Map) in.readObject());
  877           _props = (Map) in.readObject();
  878           _globals = in.readBoolean();
  879       }
  880   
  881       /**
  882        * Implementation of the {@link Externalizable} interface to write
  883        * the properties returned by {@link #toProperties}.
  884        */
  885       public void writeExternal(ObjectOutput out) throws IOException {
  886           out.writeObject(toProperties(true));
  887           out.writeObject(_props);
  888           out.writeBoolean(_globals);
  889       }
  890   
  891       /**
  892        * Uses {@link #toProperties} and {@link #fromProperties} to clone
  893        * configuration.
  894        */
  895       public Object clone() {
  896           try {
  897               Constructor cons = getClass().getConstructor
  898                   (new Class[]{ boolean.class });
  899               ConfigurationImpl clone = (ConfigurationImpl) cons.newInstance
  900                   (new Object[]{ Boolean.FALSE });
  901               clone.fromProperties(toProperties(true));
  902               clone._props = (_props == null) ? null : new HashMap(_props);
  903               clone._globals = _globals;
  904               return clone;
  905           } catch (RuntimeException re) {
  906               throw re;
  907           } catch (Exception e) {
  908               throw new ParseException(e);
  909           }
  910       }
  911   
  912       public boolean removeValue(Value val) {
  913           if (!_vals.remove(val))
  914               return false;
  915           val.setListener(null);
  916           return true;
  917       }
  918   
  919       public Value addValue(Value val) {
  920           _vals.add(val);
  921           val.setListener(this);
  922           return val;
  923       }
  924   
  925       /**
  926        * Add the given value to the set of configuration properties.
  927        */
  928       public StringValue addString(String property) {
  929           StringValue val = new StringValue(property);
  930           addValue(val);
  931           return val;
  932       }
  933   
  934       /**
  935        * Add the given value to the set of configuration properties.
  936        */
  937       public FileValue addFile(String property) {
  938           FileValue val = new FileValue(property);
  939           addValue(val);
  940           return val;
  941       }
  942   
  943       /**
  944        * Add the given value to the set of configuration properties.
  945        */
  946       public IntValue addInt(String property) {
  947           IntValue val = new IntValue(property);
  948           addValue(val);
  949           return val;
  950       }
  951   
  952       /**
  953        * Add the given value to the set of configuration properties.
  954        */
  955       public DoubleValue addDouble(String property) {
  956           DoubleValue val = new DoubleValue(property);
  957           addValue(val);
  958           return val;
  959       }
  960   
  961       /**
  962        * Add the given value to the set of configuration properties.
  963        */
  964       public BooleanValue addBoolean(String property) {
  965           BooleanValue val = new BooleanValue(property);
  966           addValue(val);
  967           val.setDefault("false");
  968           return val;
  969       }
  970   
  971       /**
  972        * Add the given value to the set of configuration properties.
  973        */
  974       public StringListValue addStringList(String property) {
  975           StringListValue val = new StringListValue(property);
  976           addValue(val);
  977           return val;
  978       }
  979   
  980       /**
  981        * Add the given value to the set of configuration properties.
  982        */
  983       public ObjectValue addObject(String property) {
  984           ObjectValue val = new ObjectValue(property);
  985           addValue(val);
  986           return val;
  987       }
  988   
  989       /**
  990        * Add the given value to the set of configuration properties.
  991        */
  992       public PluginValue addPlugin(String property, boolean singleton) {
  993           PluginValue val = new PluginValue(property, singleton);
  994           addValue(val);
  995           return val;
  996       }
  997   
  998       /**
  999        * Add the given value to the set of configuration properties.
 1000        */
 1001       public PluginListValue addPluginList(String property) {
 1002           PluginListValue val = new PluginListValue(property);
 1003           addValue(val);
 1004           return val;
 1005       }
 1006   }

Save This Page
Home » apache-openjpa-1.1.0-source » org.apache.openjpa.lib » conf » [javadoc | source]