Home » openejb-3.1.2-src » org.apache » openejb » persistence » [javadoc | source]

    1   /**
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *     http://www.apache.org/licenses/LICENSE-2.0
   10    *
   11    *  Unless required by applicable law or agreed to in writing, software
   12    *  distributed under the License is distributed on an "AS IS" BASIS,
   13    *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    *  See the License for the specific language governing permissions and
   15    *  limitations under the License.
   16    */
   17   package org.apache.openejb.persistence;
   18   
   19   import org.apache.openejb.core.TempClassLoader;
   20   import org.apache.openejb.javaagent.Agent;
   21   import org.xml.sax.Attributes;
   22   import org.xml.sax.InputSource;
   23   import org.xml.sax.SAXException;
   24   import org.xml.sax.helpers.DefaultHandler;
   25   
   26   import javax.persistence.EntityManagerFactory;
   27   import javax.persistence.spi.PersistenceProvider;
   28   import javax.sql.DataSource;
   29   import javax.xml.parsers.ParserConfigurationException;
   30   import javax.xml.parsers.SAXParser;
   31   import javax.xml.parsers.SAXParserFactory;
   32   import java.io.IOException;
   33   import java.io.InputStream;
   34   import java.io.PrintWriter;
   35   import java.lang.instrument.ClassFileTransformer;
   36   import java.lang.instrument.IllegalClassFormatException;
   37   import java.lang.instrument.Instrumentation;
   38   import java.net.URL;
   39   import java.security.ProtectionDomain;
   40   import java.sql.Connection;
   41   import java.sql.SQLException;
   42   import java.util.ArrayList;
   43   import java.util.Collections;
   44   import java.util.HashMap;
   45   import java.util.HashSet;
   46   import java.util.Map;
   47   import java.util.Properties;
   48   import java.util.Set;
   49   import java.util.Arrays;
   50   import java.util.List;
   51   
   52   /**
   53    * The goal of this class is to support persistence providers that need to do
   54    * byte code enhancement in embedded environments like JUnit where all the
   55    * entity classes are right on the system classpath and likely to be loaded
   56    * before OpenEJB boots.  The org.apache.openejb.javaagent.Agent class calls
   57    * the bootstrap() method of this class the first time it finds a classloader
   58    * that has the openejb-core jar.  We then do a quick scan of the classpath
   59    * looking for persistence.xml files and attempt to get the related persistence
   60    * providers to setup the correct byte code enhancing for the classes listed
   61    * in the persistence.xml files.
   62    *
   63    * @version $Rev: 711304 $ $Date: 2008-11-04 08:40:51 -0800 (Tue, 04 Nov 2008) $
   64    */
   65   public class PersistenceBootstrap {
   66   
   67       private static final String defaultProvider = "org.apache.openjpa.persistence.PersistenceProviderImpl";
   68       private static boolean debug;
   69   
   70       public static void bootstrap(ClassLoader classLoader) {
   71           Properties args = getAgentArgs(classLoader);
   72   
   73           debug = (args.getProperty("debug", "false").equalsIgnoreCase("true"));
   74           boolean enabled = (args.getProperty("enabled", "true").equalsIgnoreCase("true"));
   75   
   76           if (!enabled) {
   77               debug("disabled");
   78               return;
   79           }
   80   
   81           try {
   82               debug("searching for persistence.xml files");
   83   
   84               ArrayList<URL> urls = Collections.list(classLoader.getResources("META-INF/persistence.xml"));
   85   
   86               if (urls.size() == 0) {
   87                   debug("no persistence.xml files found");
   88                   return;
   89               }
   90   
   91               Map<String, Unit> units = new HashMap<String, Unit>();
   92   
   93               for (URL url : urls) {
   94                   String urlPath = url.toExternalForm();
   95                   debug("found " + urlPath);
   96                   try {
   97                       InputStream in = url.openStream();
   98                       try {
   99                           collectUnits(in, units, args);
  100                       } catch (Throwable e) {
  101                           debug("failed to read " + urlPath, e);
  102                           in.close();
  103                       }
  104                   } catch (Throwable e) {
  105                       debug("failed to read " + urlPath, e);
  106                   }
  107               }
  108   
  109               for (Unit unit : units.values()) {
  110                   String provider = unit.provider;
  111                   String extraClassesKey = provider + "@classes";
  112                   String unitNameKey = provider + "@unitName";
  113                   String unitName = args.getProperty(unitNameKey, "classpath-bootstrap");
  114   
  115                   String classes = args.getProperty(extraClassesKey);
  116                   if (classes != null) {
  117                       debug("parsing value of " + extraClassesKey);
  118   
  119                       try {
  120                           List<String> list = Arrays.asList(classes.split("[ \n\r\t,]"));
  121                           unit.classes.addAll(list);
  122                       } catch (Exception e) {
  123                           debug("cannot parse: " + classes, e);
  124                       }
  125                   }
  126                   try {
  127                       // Hibernate doesn't use byte code modification
  128                       if (provider.startsWith("org.hibernate")) {
  129                           debug("skipping: " + provider);
  130                           continue;
  131                       } else {
  132                           debug("starting: " + provider);
  133                       }
  134   
  135                       PersistenceUnitInfoImpl info = new PersistenceUnitInfoImpl(new Handler());
  136                       info.setManagedClassNames(new ArrayList(unit.classes));
  137                       info.setPersistenceProviderClassName(unit.provider);
  138                       info.setProperties(new Properties());
  139                       info.setId(unitName);
  140                       info.setPersistenceUnitName(unitName);
  141                       info.setRootUrlAndJarUrls("", Collections.EMPTY_LIST);
  142                       info.setJtaDataSource(new NullDataSource());
  143                       info.setNonJtaDataSource(new NullDataSource());
  144                       info.setExcludeUnlistedClasses(true);
  145                       info.setClassLoader(classLoader);
  146   
  147                       for (String name : unit.classes) {
  148                           debug("class " + name);
  149                       }
  150                       Class clazz = classLoader.loadClass(unit.provider);
  151                       PersistenceProvider persistenceProvider = (PersistenceProvider) clazz.newInstance();
  152   
  153                       // Create entity manager factory
  154                       EntityManagerFactory emf = persistenceProvider.createContainerEntityManagerFactory(info, new HashMap());
  155                       debug("success: " + provider);
  156                   } catch (Throwable e) {
  157                       debug("failed: " + provider, e);
  158                   }
  159               }
  160           } catch (Throwable t) {
  161               debug("error: ", t);
  162           }
  163       }
  164   
  165       private static void debug(String x) {
  166           if (debug) System.out.println("[PersistenceBootstrap] " + x);
  167       }
  168   
  169       private static void debug(String x, Throwable t) {
  170           if (debug) {
  171               System.out.println(x);
  172               t.printStackTrace();
  173           }
  174       }
  175   
  176       private static Properties getAgentArgs(ClassLoader classLoader) {
  177           Properties properties = new Properties();
  178           String args = Agent.getAgentArgs();
  179           if (args != null && args.length() != 0) {
  180               for (String string : args.split("[ ,:&]")) {
  181                   String[] strings = string.split("=");
  182                   if (strings != null && strings.length == 2) {
  183                       properties.put(strings[0], strings[1]);
  184                   }
  185               }
  186   
  187               debug = (properties.getProperty("debug", "false").equalsIgnoreCase("true"));
  188   
  189           }
  190   
  191           try {
  192               URL resource = classLoader.getResource("PersistenceBootstrap.properties");
  193               if (resource != null) {
  194                   debug("found PersistenceBootstrap.properties file");
  195                   InputStream in = resource.openStream();
  196                   try {
  197                       properties.load(in);
  198                   } finally {
  199                       if (in != null) in.close();
  200                   }
  201               }
  202           } catch (Throwable e) {
  203               debug("can't read PersistenceBootstrap.properties file", e);
  204           }
  205   
  206           return properties;
  207       }
  208   
  209       private static void collectUnits(InputStream in, final Map<String, Unit> units, final Properties args) throws ParserConfigurationException, SAXException, IOException {
  210           InputSource inputSource = new InputSource(in);
  211   
  212           SAXParserFactory factory = SAXParserFactory.newInstance();
  213           factory.setNamespaceAware(true);
  214           factory.setValidating(false);
  215           SAXParser parser = factory.newSAXParser();
  216   
  217   
  218           parser.parse(inputSource, new DefaultHandler() {
  219               private StringBuilder characters = new StringBuilder();
  220               private Unit unit;
  221   
  222               public void startElement(String uri, String localName, String qName, Attributes attributes) {
  223                   characters = new StringBuilder(100);
  224   
  225                   if (localName.equals("persistence-unit")) startPersistenceUnit(uri, localName, qName, attributes);
  226               }
  227   
  228               public void startPersistenceUnit(String uri, String localName, String qName, Attributes attributes) {
  229                   String unitName = attributes.getValue(null, "name");
  230                   unit = new Unit(unitName);
  231               }
  232   
  233               public void characters(char ch[], int start, int length) {
  234                   String text = new String(ch, start, length);
  235                   characters.append(text.trim());
  236               }
  237   
  238               public void endElement(String uri, String localName, String qName) {
  239                   if (localName.equals("persistence-unit")) endPersistenceUnit(uri, localName, qName);
  240                   else if (localName.equals("provider")) endProvider(uri, localName, qName);
  241                   else if (localName.equals("class")) endClass(uri, localName, qName);
  242               }
  243   
  244               public void endPersistenceUnit(String uri, String localName, String qName) {
  245                   if (args.getProperty(unit.name + "@skip", "false").equalsIgnoreCase("true")) {
  246                       debug("skipping unit " + unit.name);
  247                   } else {
  248                       debug("adding unit " + unit.name);
  249   
  250                       if (unit.provider == null) {
  251                           unit.provider = defaultProvider;
  252                       }
  253   
  254                       Unit u = units.get(unit.provider);
  255                       if (u == null) {
  256                           units.put(unit.provider, unit);
  257                       } else {
  258                           u.classes.addAll(unit.classes);
  259                       }
  260                   }
  261   
  262                   unit = null;
  263               }
  264   
  265               public void endProvider(String uri, String localName, String qName) {
  266                   unit.provider = characters.toString();
  267               }
  268   
  269               public void endClass(String uri, String localName, String qName) {
  270                   unit.classes.add(characters.toString());
  271               }
  272           });
  273       }
  274   
  275       private static class Unit {
  276           private String provider;
  277           private final Set<String> classes = new HashSet<String>();
  278           private final String name;
  279   
  280           public Unit(String name) {
  281               this.name = name;
  282           }
  283   
  284           public String getProvider() {
  285               return provider;
  286           }
  287   
  288           public void setProvider(String provider) {
  289               this.provider = provider;
  290           }
  291       }
  292   
  293       private static class Handler implements PersistenceClassLoaderHandler {
  294           public void addTransformer(String unitId, ClassLoader classLoader, ClassFileTransformer classFileTransformer) {
  295               Instrumentation instrumentation = Agent.getInstrumentation();
  296               if (instrumentation != null) {
  297                   instrumentation.addTransformer(new Transformer(classFileTransformer));
  298               }
  299           }
  300   
  301           public void destroy(String unitId) {
  302           }
  303   
  304           public ClassLoader getNewTempClassLoader(ClassLoader classLoader) {
  305               return new TempClassLoader(classLoader);
  306           }
  307   
  308       }
  309   
  310       public static class Transformer implements ClassFileTransformer {
  311           private final ClassFileTransformer transformer;
  312   
  313           public Transformer(ClassFileTransformer transformer) {
  314               this.transformer = transformer;
  315           }
  316   
  317           public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
  318               try {
  319                   byte[] bytes = transformer.transform(loader, className, classBeingRedefined, protectionDomain, classfileBuffer);
  320                   if (bytes != null) {
  321                       debug("enhanced " + className);
  322                   }
  323                   return bytes;
  324               } catch (Throwable e) {
  325                   e.printStackTrace();
  326                   return null;
  327               }
  328           }
  329       }
  330   
  331       private static class NullDataSource implements DataSource {
  332           public Connection getConnection() throws SQLException {
  333               return null;
  334           }
  335   
  336           public Connection getConnection(String username, String password) throws SQLException {
  337               return null;
  338           }
  339   
  340           public int getLoginTimeout() throws SQLException {
  341               return 0;
  342           }
  343   
  344           public PrintWriter getLogWriter() throws SQLException {
  345               return null;
  346           }
  347   
  348           public void setLoginTimeout(int seconds) throws SQLException {
  349           }
  350   
  351           public void setLogWriter(PrintWriter out) throws SQLException {
  352           }
  353   
  354           public boolean isWrapperFor(Class<?> iface) throws SQLException {
  355               return false;
  356           }
  357   
  358           public <T> T unwrap(Class<T> iface) throws SQLException {
  359               throw new SQLException();
  360           }
  361       }
  362   }

Home » openejb-3.1.2-src » org.apache » openejb » persistence » [javadoc | source]