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.lang.instrument.ClassFileTransformer;
22 import java.lang.instrument.IllegalClassFormatException;
23 import java.security.ProtectionDomain;
24 import java.util.Map;
25 import javax.persistence.EntityManager;
26 import javax.persistence.spi.ClassTransformer;
27 import javax.persistence.spi.PersistenceProvider;
28 import javax.persistence.spi.PersistenceUnitInfo;
29
30 import org.apache.openjpa.conf.BrokerValue;
31 import org.apache.openjpa.conf.OpenJPAConfiguration;
32 import org.apache.openjpa.conf.OpenJPAConfigurationImpl;
33 import org.apache.openjpa.enhance.PCClassFileTransformer;
34 import org.apache.openjpa.kernel.Bootstrap;
35 import org.apache.openjpa.kernel.BrokerFactory;
36 import org.apache.openjpa.lib.conf.Configuration;
37 import org.apache.openjpa.lib.conf.ConfigurationProvider;
38 import org.apache.openjpa.lib.conf.Configurations;
39 import org.apache.openjpa.lib.log.Log;
40 import org.apache.openjpa.lib.util.Localizer;
41 import org.apache.openjpa.meta.MetaDataModes;
42 import org.apache.openjpa.meta.MetaDataRepository;
43 import org.apache.openjpa.util.ClassResolver;
44
45
46 /**
47 * Bootstrapping class that allows the creation of a stand-alone
48 * {@link EntityManager}.
49 *
50 * @see javax.persistence.Persistence#createEntityManagerFactory(String,Map)
51 * @published
52 */
53 public class PersistenceProviderImpl
54 implements PersistenceProvider {
55
56 static final String CLASS_TRANSFORMER_OPTIONS = "ClassTransformerOptions";
57 private static final String EMF_POOL = "EntityManagerFactoryPool";
58
59 private static final Localizer _loc = Localizer.forPackage(
60 PersistenceProviderImpl.class);
61
62 /**
63 * Loads the entity manager specified by <code>name</code>, applying
64 * the properties in <code>m</code> as overrides to the properties defined
65 * in the XML configuration file for <code>name</code>. If <code>name</code>
66 * is <code>null</code>, this method loads the XML in the resource
67 * identified by <code>resource</code>, and uses the first resource found
68 * when doing this lookup, regardless of the name specified in the XML
69 * resource or the name of the jar that the resource is contained in.
70 * This does no pooling of EntityManagersFactories.
71 */
72 public OpenJPAEntityManagerFactory createEntityManagerFactory(String name,
73 String resource, Map m) {
74 PersistenceProductDerivation pd = new PersistenceProductDerivation();
75 try {
76 Object poolValue = Configurations.removeProperty(EMF_POOL, m);
77 ConfigurationProvider cp = pd.load(resource, name, m);
78 if (cp == null)
79 return null;
80
81 BrokerFactory factory = getBrokerFactory(cp, poolValue, null);
82 return JPAFacadeHelper.toEntityManagerFactory(factory);
83 } catch (Exception e) {
84 throw PersistenceExceptions.toPersistenceException(e);
85 }
86 }
87
88 private BrokerFactory getBrokerFactory(ConfigurationProvider cp,
89 Object poolValue, ClassLoader loader) {
90 // handle "true" and "false"
91 if (poolValue instanceof String
92 && ("true".equalsIgnoreCase((String) poolValue)
93 || "false".equalsIgnoreCase((String) poolValue)))
94 poolValue = Boolean.valueOf((String) poolValue);
95
96 if (poolValue != null && !(poolValue instanceof Boolean)) {
97 // we only support boolean settings for this option currently.
98 throw new IllegalArgumentException(poolValue.toString());
99 }
100
101 if (poolValue == null || !((Boolean) poolValue).booleanValue())
102 return Bootstrap.newBrokerFactory(cp, loader);
103 else
104 return Bootstrap.getBrokerFactory(cp, loader);
105 }
106
107 public OpenJPAEntityManagerFactory createEntityManagerFactory(String name,
108 Map m) {
109 return createEntityManagerFactory(name, null, m);
110 }
111
112 public OpenJPAEntityManagerFactory createContainerEntityManagerFactory(
113 PersistenceUnitInfo pui, Map m) {
114 PersistenceProductDerivation pd = new PersistenceProductDerivation();
115 try {
116 Object poolValue = Configurations.removeProperty(EMF_POOL, m);
117 ConfigurationProvider cp = pd.load(pui, m);
118 if (cp == null)
119 return null;
120
121 // add enhancer
122 Exception transformerException = null;
123 String ctOpts = (String) Configurations.getProperty
124 (CLASS_TRANSFORMER_OPTIONS, pui.getProperties());
125 try {
126 pui.addTransformer(new ClassTransformerImpl(cp, ctOpts,
127 pui.getNewTempClassLoader(), newConfigurationImpl()));
128 } catch (Exception e) {
129 // fail gracefully
130 transformerException = e;
131 }
132
133 // if the BrokerImpl hasn't been specified, switch to the
134 // non-finalizing one, since anything claiming to be a container
135 // should be doing proper resource management.
136 if (!Configurations.containsProperty(BrokerValue.KEY,
137 cp.getProperties())) {
138 cp.addProperty("openjpa." + BrokerValue.KEY,
139 getDefaultBrokerAlias());
140 }
141
142 BrokerFactory factory = getBrokerFactory(cp, poolValue,
143 pui.getClassLoader());
144 if (transformerException != null) {
145 Log log = factory.getConfiguration().getLog(
146 OpenJPAConfiguration.LOG_RUNTIME);
147 if (log.isTraceEnabled()) {
148 log.warn(
149 _loc.get("transformer-registration-error-ex", pui),
150 transformerException);
151 } else {
152 log.warn(
153 _loc.get("transformer-registration-error", pui));
154 }
155 }
156 return JPAFacadeHelper.toEntityManagerFactory(factory);
157 } catch (Exception e) {
158 throw PersistenceExceptions.toPersistenceException(e);
159 }
160 }
161
162 /*
163 * Returns a default Broker alias to be used when no openjpa.BrokerImpl
164 * is specified. This method allows PersistenceProvider subclass to
165 * override the default broker alias.
166 */
167 protected String getDefaultBrokerAlias() {
168 return BrokerValue.NON_FINALIZING_ALIAS;
169 }
170
171 /*
172 * Return a new instance of Configuration subclass used by entity
173 * enhancement in ClassTransformerImpl. If OpenJPAConfigurationImpl
174 * instance is used, configuration options declared in configuration
175 * sub-class will not be recognized and a warning is posted in the log.
176 */
177 protected OpenJPAConfiguration newConfigurationImpl() {
178 return new OpenJPAConfigurationImpl();
179 }
180
181 /**
182 * Java EE 5 class transformer.
183 */
184 private static class ClassTransformerImpl
185 implements ClassTransformer {
186
187 private final ClassFileTransformer _trans;
188
189 private ClassTransformerImpl(ConfigurationProvider cp, String props,
190 final ClassLoader tmpLoader, OpenJPAConfiguration conf) {
191 cp.setInto(conf);
192 // use the tmp loader for everything
193 conf.setClassResolver(new ClassResolver() {
194 public ClassLoader getClassLoader(Class context,
195 ClassLoader env) {
196 return tmpLoader;
197 }
198 });
199 conf.setReadOnly(Configuration.INIT_STATE_FREEZING);
200
201 MetaDataRepository repos = conf.getMetaDataRepositoryInstance();
202 repos.setResolve(MetaDataModes.MODE_MAPPING, false);
203 _trans = new PCClassFileTransformer(repos,
204 Configurations.parseProperties(props), tmpLoader);
205 }
206
207 public byte[] transform(ClassLoader cl, String name,
208 Class<?> previousVersion, ProtectionDomain pd, byte[] bytes)
209 throws IllegalClassFormatException {
210 return _trans.transform(cl, name, previousVersion, pd, bytes);
211 }
212 }
213 }