Home » apache-openjpa-1.1.0-source » org.apache.openjpa » persistence » [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.persistence;
   20   
   21   import java.io.File;
   22   import java.io.IOException;
   23   import java.net.MalformedURLException;
   24   import java.net.URL;
   25   import java.security.AccessController;
   26   import java.security.PrivilegedActionException;
   27   import java.util.ArrayList;
   28   import java.util.Collections;
   29   import java.util.Enumeration;
   30   import java.util.List;
   31   import java.util.Map;
   32   import java.util.MissingResourceException;
   33   import javax.persistence.spi.PersistenceUnitInfo;
   34   import javax.persistence.spi.PersistenceUnitTransactionType;
   35   
   36   import org.apache.commons.lang.StringUtils;
   37   import org.apache.openjpa.conf.OpenJPAConfiguration;
   38   import org.apache.openjpa.conf.OpenJPAConfigurationImpl;
   39   import org.apache.openjpa.conf.OpenJPAProductDerivation;
   40   import org.apache.openjpa.lib.conf.AbstractProductDerivation;
   41   import org.apache.openjpa.lib.conf.Configuration;
   42   import org.apache.openjpa.lib.conf.ConfigurationProvider;
   43   import org.apache.openjpa.lib.conf.Configurations;
   44   import org.apache.openjpa.lib.conf.MapConfigurationProvider;
   45   import org.apache.openjpa.lib.conf.ProductDerivations;
   46   import org.apache.openjpa.lib.log.Log;
   47   import org.apache.openjpa.lib.meta.XMLMetaDataParser;
   48   import org.apache.openjpa.lib.util.J2DoPrivHelper;
   49   import org.apache.openjpa.lib.util.Localizer;
   50   import org.xml.sax.Attributes;
   51   import org.xml.sax.SAXException;
   52   
   53   /**
   54    * Sets JPA specification defaults and parses JPA specification XML files.
   55    * 
   56    * For globals, looks in <code>openjpa.properties</code> system property for
   57    * the location of a file to parse. If no system property is defined, the
   58    * default resource location of <code>META-INF/openjpa.xml</code> is used.
   59    *
   60    * For defaults, looks for <code>META-INF/persistence.xml</code>.
   61    * Within <code>persistence.xml</code>, look for the named persistence unit, or
   62    * if no name given, an OpenJPA unit (preferring an unnamed OpenJPA unit to 
   63    * a named one).
   64    *
   65    * @author Abe White
   66    * @nojavadoc
   67    */
   68   public class PersistenceProductDerivation 
   69       extends AbstractProductDerivation
   70       implements OpenJPAProductDerivation {
   71   
   72       public static final String SPEC_JPA = "jpa";
   73       public static final String ALIAS_EJB = "ejb";
   74       public static final String RSRC_GLOBAL = "META-INF/openjpa.xml";
   75       public static final String RSRC_DEFAULT = "META-INF/persistence.xml";
   76   
   77       private static final Localizer _loc = Localizer.forPackage
   78           (PersistenceProductDerivation.class);
   79   
   80       public void putBrokerFactoryAliases(Map m) {
   81       }
   82   
   83       public int getType() {
   84           return TYPE_SPEC;
   85       }
   86   
   87       @Override
   88       public void validate()
   89           throws Exception {
   90           // make sure JPA is available
   91           AccessController.doPrivileged(J2DoPrivHelper.getClassLoaderAction(
   92               javax.persistence.EntityManagerFactory.class));
   93       }
   94       
   95       @Override
   96       public boolean beforeConfigurationLoad(Configuration c) {
   97           if (!(c instanceof OpenJPAConfigurationImpl))
   98               return false;
   99           
  100           OpenJPAConfigurationImpl conf = (OpenJPAConfigurationImpl) c;
  101           conf.metaFactoryPlugin.setAlias(ALIAS_EJB,
  102               PersistenceMetaDataFactory.class.getName());
  103           conf.metaFactoryPlugin.setAlias(SPEC_JPA,
  104               PersistenceMetaDataFactory.class.getName());
  105           
  106           conf.addValue(new EntityManagerFactoryValue());
  107           return true;
  108       }
  109   
  110       @Override
  111       public boolean afterSpecificationSet(Configuration c) {
  112         if (!(c instanceof OpenJPAConfigurationImpl)
  113            || !SPEC_JPA.equals(((OpenJPAConfiguration) c).getSpecification()))
  114             return false;
  115    
  116           OpenJPAConfigurationImpl conf = (OpenJPAConfigurationImpl) c;
  117           conf.metaFactoryPlugin.setDefault(SPEC_JPA);
  118           conf.metaFactoryPlugin.setString(SPEC_JPA);
  119           conf.lockManagerPlugin.setDefault("version");
  120           conf.lockManagerPlugin.setString("version");
  121           conf.nontransactionalWrite.setDefault("true");
  122           conf.nontransactionalWrite.set(true);
  123           return true;
  124       }
  125   
  126       /**
  127        * Load configuration from the given persistence unit with the specified
  128        * user properties.
  129        */
  130       public ConfigurationProvider load(PersistenceUnitInfo pinfo, Map m)
  131           throws IOException {
  132           if (pinfo == null)
  133               return null;
  134           if (!isOpenJPAPersistenceProvider(pinfo, null)) {
  135               warnUnknownProvider(pinfo);
  136               return null;
  137           }
  138   
  139           ConfigurationProviderImpl cp = new ConfigurationProviderImpl();
  140           cp.addProperties(PersistenceUnitInfoImpl.toOpenJPAProperties(pinfo));
  141           cp.addProperties(m);
  142           if (pinfo instanceof PersistenceUnitInfoImpl) {
  143               PersistenceUnitInfoImpl impl = (PersistenceUnitInfoImpl) pinfo;
  144               if (impl.getPersistenceXmlFileUrl() != null)
  145                   cp.setSource(impl.getPersistenceXmlFileUrl().toString());
  146           }
  147           return cp;
  148       }
  149   
  150       /**
  151        * Load configuration from the given resource and unit names, which may
  152        * be null.
  153        */
  154       public ConfigurationProvider load(String rsrc, String name, Map m)
  155           throws IOException {
  156           boolean explicit = !StringUtils.isEmpty(rsrc);
  157           if (!explicit)
  158               rsrc = RSRC_DEFAULT;
  159           
  160           ConfigurationProviderImpl cp = new ConfigurationProviderImpl();
  161           Boolean ret = load(cp, rsrc, name, m, null, explicit);
  162           if (ret != null)
  163               return (ret.booleanValue()) ? cp : null;
  164           if (explicit)
  165               return null;
  166   
  167           // persistence.xml does not exist; just load map
  168           PersistenceUnitInfoImpl pinfo = new PersistenceUnitInfoImpl();
  169           pinfo.fromUserProperties(m);
  170           if (!isOpenJPAPersistenceProvider(pinfo, null)) {
  171               warnUnknownProvider(pinfo);
  172               return null;
  173           }
  174           cp.addProperties(pinfo.toOpenJPAProperties());
  175           return cp;
  176       }
  177   
  178       @Override
  179       public ConfigurationProvider load(String rsrc, String anchor, 
  180           ClassLoader loader)
  181           throws IOException {
  182           if (rsrc != null && !rsrc.endsWith(".xml"))
  183               return null;
  184           ConfigurationProviderImpl cp = new ConfigurationProviderImpl();
  185           if (load(cp, rsrc, anchor, null, loader, true) == Boolean.TRUE)
  186               return cp;
  187           return null;
  188       }
  189   
  190       @Override
  191       public ConfigurationProvider load(File file, String anchor) 
  192           throws IOException {
  193           if (!file.getName().endsWith(".xml"))
  194               return null;
  195   
  196           ConfigurationParser parser = new ConfigurationParser(null);
  197           parser.parse(file);
  198           return load(findUnit((List<PersistenceUnitInfoImpl>) 
  199               parser.getResults(), anchor, null), null);
  200       }
  201   
  202       @Override
  203       public String getDefaultResourceLocation() {
  204           return RSRC_DEFAULT;
  205       }
  206   
  207       @Override
  208       public List getAnchorsInFile(File file) throws IOException {
  209           ConfigurationParser parser = new ConfigurationParser(null);
  210           try {
  211               parser.parse(file);
  212               return getUnitNames(parser);
  213           } catch (IOException e) {
  214               // not all configuration files are XML; return null if unparsable
  215               return null;
  216           }
  217       }
  218   
  219       private List<String> getUnitNames(ConfigurationParser parser) {
  220           List<PersistenceUnitInfoImpl> units = parser.getResults();
  221           List<String> names = new ArrayList<String>();
  222           for (PersistenceUnitInfoImpl unit : units)
  223               names.add(unit.getPersistenceUnitName());
  224           return names;
  225       }
  226   
  227       @Override
  228       public List getAnchorsInResource(String resource) throws Exception {
  229           ConfigurationParser parser = new ConfigurationParser(null);
  230           try {
  231               ClassLoader loader = (ClassLoader) AccessController.doPrivileged(
  232                   J2DoPrivHelper.getContextClassLoaderAction());
  233               List<URL> urls = getResourceURLs(resource, loader);
  234               if (urls != null) {
  235                   for (URL url : urls) {
  236                       parser.parse(url);
  237                   }
  238               }
  239               return getUnitNames(parser);
  240           } catch (IOException e) {
  241               // not all configuration files are XML; return null if unparsable
  242               return null;
  243           }
  244       }
  245   
  246       @Override
  247       public ConfigurationProvider loadGlobals(ClassLoader loader)
  248           throws IOException {
  249           String[] prefixes = ProductDerivations.getConfigurationPrefixes();
  250           String rsrc = null;
  251           for (int i = 0; i < prefixes.length && StringUtils.isEmpty(rsrc); i++)
  252              rsrc = (String) AccessController.doPrivileged(J2DoPrivHelper
  253                   .getPropertyAction(prefixes[i] + ".properties")); 
  254           boolean explicit = !StringUtils.isEmpty(rsrc);
  255           String anchor = null;
  256           int idx = (!explicit) ? -1 : rsrc.lastIndexOf('#');
  257           if (idx != -1) {
  258               // separate name from <resrouce>#<name> string
  259               if (idx < rsrc.length() - 1)
  260                   anchor = rsrc.substring(idx + 1);
  261               rsrc = rsrc.substring(0, idx);
  262           }
  263           if (StringUtils.isEmpty(rsrc))
  264               rsrc = RSRC_GLOBAL;
  265           else if (!rsrc.endsWith(".xml"))
  266               return null;
  267   
  268           ConfigurationProviderImpl cp = new ConfigurationProviderImpl();
  269           if (load(cp, rsrc, anchor, null, loader, explicit) == Boolean.TRUE)
  270               return cp;
  271           return null;
  272       }
  273   
  274       @Override
  275       public ConfigurationProvider loadDefaults(ClassLoader loader)
  276           throws IOException {
  277           ConfigurationProviderImpl cp = new ConfigurationProviderImpl();
  278           if (load(cp, RSRC_DEFAULT, null, null, loader, false) == Boolean.TRUE)
  279               return cp;
  280           return null;
  281       }
  282   
  283       private static List<URL> getResourceURLs(String rsrc, ClassLoader loader)
  284           throws IOException {
  285           Enumeration<URL> urls = null;
  286           try {
  287               urls = (Enumeration) AccessController.doPrivileged(
  288                   J2DoPrivHelper.getResourcesAction(loader, rsrc)); 
  289               if (!urls.hasMoreElements()) {
  290                   if (!rsrc.startsWith("META-INF"))
  291                       urls = (Enumeration) AccessController.doPrivileged(
  292                           J2DoPrivHelper.getResourcesAction(
  293                               loader, "META-INF/" + rsrc)); 
  294                   if (!urls.hasMoreElements())
  295                       return null;
  296               }
  297           } catch (PrivilegedActionException pae) {
  298               throw (IOException) pae.getException();
  299           }
  300   
  301           return Collections.list(urls);
  302       }
  303   
  304       /**
  305        * Looks through the resources at <code>rsrc</code> for a configuration
  306        * file that matches <code>name</code> (or an unnamed one if
  307        * <code>name</code> is <code>null</code>), and loads the XML in the
  308        * resource into a new {@link PersistenceUnitInfo}. Then, applies the
  309        * overrides in <code>m</code>.
  310        *
  311        * @return {@link Boolean#TRUE} if the resource was loaded, null if it
  312        * does not exist, or {@link Boolean#FALSE} if it is not for OpenJPA
  313        */
  314       private Boolean load(ConfigurationProviderImpl cp, String rsrc, 
  315           String name, Map m, ClassLoader loader, boolean explicit)
  316           throws IOException {
  317           if (loader == null)
  318               loader = (ClassLoader) AccessController.doPrivileged(
  319                   J2DoPrivHelper.getContextClassLoaderAction());
  320   
  321           List<URL> urls = getResourceURLs(rsrc, loader);
  322           if (urls == null || urls.size() == 0)
  323               return null;
  324   
  325           ConfigurationParser parser = new ConfigurationParser(m);
  326           PersistenceUnitInfoImpl pinfo = parseResources(parser, urls, name, 
  327               loader);
  328           if (pinfo == null) {
  329               if (!explicit)
  330                   return Boolean.FALSE;
  331               throw new MissingResourceException(_loc.get("missing-xml-config", 
  332                   rsrc, String.valueOf(name)).getMessage(), getClass().getName(), 
  333                   rsrc);
  334           } else if (!isOpenJPAPersistenceProvider(pinfo, loader)) {
  335               if (!explicit) {
  336                   warnUnknownProvider(pinfo);
  337                   return Boolean.FALSE;
  338               }
  339               throw new MissingResourceException(_loc.get("unknown-provider", 
  340                   rsrc, name, pinfo.getPersistenceProviderClassName()).
  341                   getMessage(), getClass().getName(), rsrc);
  342           }
  343           cp.addProperties(pinfo.toOpenJPAProperties());
  344           cp.setSource(pinfo.getPersistenceXmlFileUrl().toString());
  345           return Boolean.TRUE;
  346       }
  347   
  348       /**
  349        * Parse resources at the given location. Searches for a
  350        * PersistenceUnitInfo with the requested name, or an OpenJPA unit if
  351        * no name given (preferring an unnamed OpenJPA unit to a named one).
  352        */
  353       private PersistenceUnitInfoImpl parseResources(ConfigurationParser parser,
  354           List<URL> urls, String name, ClassLoader loader)
  355           throws IOException {
  356           List<PersistenceUnitInfoImpl> pinfos = 
  357               new ArrayList<PersistenceUnitInfoImpl>();
  358           for (URL url : urls) {
  359               parser.parse(url);
  360               pinfos.addAll((List<PersistenceUnitInfoImpl>) parser.getResults());
  361           }
  362           return findUnit(pinfos, name, loader);
  363       }
  364   
  365       /**
  366        * Find the unit with the given name, or an OpenJPA unit if no name is
  367        * given (preferring an unnamed OpenJPA unit to a named one).
  368        */
  369       private PersistenceUnitInfoImpl findUnit(List<PersistenceUnitInfoImpl> 
  370           pinfos, String name, ClassLoader loader) {
  371           PersistenceUnitInfoImpl ojpa = null;
  372           for (PersistenceUnitInfoImpl pinfo : pinfos) {
  373               // found named unit?
  374               if (name != null) {
  375                   if (name.equals(pinfo.getPersistenceUnitName()))
  376                       return pinfo;
  377                   continue;
  378               }
  379   
  380               if (isOpenJPAPersistenceProvider(pinfo, loader)) {
  381                   // if no name given and found unnamed unit, return it.  
  382                   // otherwise record as default unit unless we find a 
  383                   // better match later
  384                   if (StringUtils.isEmpty(pinfo.getPersistenceUnitName()))
  385                       return pinfo;
  386                   if (ojpa == null)
  387                       ojpa = pinfo;
  388               }
  389           }
  390           return ojpa;
  391       }
  392   
  393       /**
  394        * Return whether the given persistence unit uses an OpenJPA provider.
  395        */
  396       private static boolean isOpenJPAPersistenceProvider
  397           (PersistenceUnitInfo pinfo, ClassLoader loader) {
  398           String provider = pinfo.getPersistenceProviderClassName();
  399           if (StringUtils.isEmpty(provider) 
  400               || PersistenceProviderImpl.class.getName().equals(provider))
  401               return true;
  402   
  403           if (loader == null)
  404               loader = (ClassLoader) AccessController.doPrivileged(
  405                   J2DoPrivHelper.getContextClassLoaderAction());
  406           try {
  407               if (PersistenceProviderImpl.class.isAssignableFrom
  408                   (Class.forName(provider, false, loader)))
  409                   return true;
  410           } catch (Throwable t) {
  411               log(_loc.get("unloadable-provider", provider, t).getMessage());
  412               return false;
  413           }
  414           return false;
  415       }
  416   
  417       /**
  418        * Warn the user that we could only find an unrecognized persistence 
  419        * provider.
  420        */
  421       private static void warnUnknownProvider(PersistenceUnitInfo pinfo) {
  422           log(_loc.get("unrecognized-provider", 
  423               pinfo.getPersistenceProviderClassName()).getMessage());
  424       }
  425       
  426       /**
  427        * Log a message.   
  428        */
  429       private static void log(String msg) {
  430           // at this point logging isn't configured yet
  431           System.err.println(msg);
  432       }
  433   
  434       /**
  435        * Custom configuration provider.   
  436        */
  437       public static class ConfigurationProviderImpl
  438           extends MapConfigurationProvider {
  439   
  440           private String _source;
  441   
  442           public ConfigurationProviderImpl() {
  443           }
  444   
  445           public ConfigurationProviderImpl(Map props) {
  446               super(props);
  447           }
  448   
  449           /**
  450            * Set the source of information in this provider.
  451            */
  452           public void setSource(String source) {
  453               _source = source;
  454           }
  455   
  456           @Override
  457           public void setInto(Configuration conf) {
  458               if (conf instanceof OpenJPAConfiguration) {
  459                   OpenJPAConfiguration oconf = (OpenJPAConfiguration) conf;
  460                   oconf.setSpecification(SPEC_JPA);
  461   
  462                   // we merge several persistence.xml elements into the 
  463                   // MetaDataFactory property implicitly.  if the user has a
  464                   // global openjpa.xml with this property set, its value will
  465                   // get overwritten by our implicit setting.  so instead, combine
  466                   // the global value with our settings
  467                   String orig = oconf.getMetaDataFactory();
  468                   if (!StringUtils.isEmpty(orig)) {
  469                       String key = ProductDerivations.getConfigurationKey
  470                           ("MetaDataFactory", getProperties());
  471                       Object override = getProperties().get(key);
  472                       if (override instanceof String)
  473                           addProperty(key, Configurations.combinePlugins(orig, 
  474                               (String) override));
  475                   }
  476               }
  477   
  478               super.setInto(conf, null);
  479               Log log = conf.getConfigurationLog();
  480               if (log.isTraceEnabled()) {
  481                   String src = (_source == null) ? "?" : _source;
  482                   log.trace(_loc.get("conf-load", src, getProperties()));
  483               }
  484           }
  485       }
  486   
  487       /**
  488        * SAX handler capable of parsing an JPA persistence.xml file.
  489        * Package-protected for testing.
  490        */
  491       public static class ConfigurationParser
  492           extends XMLMetaDataParser {
  493   
  494           private final Map _map;
  495           private PersistenceUnitInfoImpl _info = null;
  496           private URL _source = null;
  497   
  498           public ConfigurationParser(Map map) {
  499               _map = map;
  500               setCaching(false);
  501               setValidating(true);
  502               setParseText(true);
  503           }
  504   
  505           @Override
  506           public void parse(URL url)
  507               throws IOException {
  508               _source = url;
  509               super.parse(url);
  510           }
  511   
  512           @Override
  513           public void parse(File file)
  514               throws IOException {
  515               try {
  516                   _source = (URL) AccessController.doPrivileged(J2DoPrivHelper
  517                       .toURLAction(file));
  518               } catch (PrivilegedActionException pae) {
  519                   throw (MalformedURLException) pae.getException();
  520               }
  521               super.parse(file);
  522           }
  523   
  524           @Override
  525           protected Object getSchemaSource() {
  526               return getClass().getResourceAsStream("persistence-xsd.rsrc");
  527           }
  528   
  529           @Override
  530           protected void reset() {
  531               super.reset();
  532               _info = null;
  533               _source = null;
  534           }
  535   
  536           protected boolean startElement(String name, Attributes attrs)
  537               throws SAXException {
  538               if (currentDepth() == 1)
  539                   startPersistenceUnit(attrs);
  540               else if (currentDepth() == 3 && "property".equals(name))
  541                   _info.setProperty(attrs.getValue("name"),
  542                       attrs.getValue("value"));
  543               return true;
  544           }
  545   
  546           protected void endElement(String name)
  547               throws SAXException {
  548               if (currentDepth() == 1) {
  549                   _info.fromUserProperties(_map);
  550                   addResult(_info);
  551               }
  552               if (currentDepth() != 2)
  553                   return;
  554   
  555               switch (name.charAt(0)) {
  556                   case 'c': // class
  557                       _info.addManagedClassName(currentText());
  558                   case 'e': // exclude-unlisted-classes
  559                       _info.setExcludeUnlistedClasses("true".equalsIgnoreCase
  560                           (currentText()));
  561                       break;
  562                   case 'j':
  563                       if ("jta-data-source".equals(name))
  564                           _info.setJtaDataSourceName(currentText());
  565                       else // jar-file
  566                       {
  567                           try {
  568                               _info.addJarFileName(currentText());
  569                           } catch (IllegalArgumentException iae) {
  570                               throw getException(iae.getMessage());
  571                           }
  572                       }
  573                       break;
  574                   case 'm': // mapping-file
  575                       _info.addMappingFileName(currentText());
  576                       break;
  577                   case 'n': // non-jta-data-source
  578                       _info.setNonJtaDataSourceName(currentText());
  579                       break;
  580                   case 'p':
  581                       if ("provider".equals(name))
  582                           _info.setPersistenceProviderClassName(currentText());
  583                       break;
  584               }
  585           }
  586   
  587           /**
  588            * Parse persistence-unit element.
  589            */
  590           private void startPersistenceUnit(Attributes attrs)
  591               throws SAXException {
  592               _info = new PersistenceUnitInfoImpl();
  593               _info.setPersistenceUnitName(attrs.getValue("name"));
  594   
  595               // we only parse this ourselves outside a container, so default
  596               // transaction type to local
  597               String val = attrs.getValue("transaction-type");
  598               if (val == null)
  599                   _info.setTransactionType
  600                       (PersistenceUnitTransactionType.RESOURCE_LOCAL);
  601               else
  602                   _info.setTransactionType(Enum.valueOf
  603                       (PersistenceUnitTransactionType.class, val));
  604   
  605               if (_source != null)
  606                   _info.setPersistenceXmlFileUrl(_source);
  607   		}
  608   	}
  609   }

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