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.reflect.Constructor;
22 import java.lang.reflect.InvocationTargetException;
23 import java.lang.reflect.Method;
24 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.LinkedList;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Properties;
30 import java.util.Set;
31 import javax.persistence.EntityManagerFactory;
32
33 import org.apache.openjpa.conf.OpenJPAConfiguration;
34 import org.apache.openjpa.enhance.Reflection;
35 import org.apache.openjpa.kernel.AutoDetach;
36 import org.apache.openjpa.kernel.Broker;
37 import org.apache.openjpa.kernel.BrokerFactory;
38 import org.apache.openjpa.kernel.DelegatingBrokerFactory;
39 import org.apache.openjpa.kernel.DelegatingFetchConfiguration;
40 import org.apache.openjpa.kernel.FetchConfiguration;
41 import org.apache.openjpa.lib.conf.Configurations;
42 import org.apache.openjpa.lib.conf.ProductDerivations;
43 import org.apache.openjpa.lib.conf.Value;
44 import org.apache.openjpa.lib.util.Localizer;
45 import org.apache.openjpa.lib.util.Closeable;
46 import org.apache.openjpa.util.OpenJPAException;
47 import serp.util.Strings;
48
49 /**
50 * Implementation of {@link EntityManagerFactory} that acts as a
51 * facade to a {@link BrokerFactory}.
52 *
53 * @author Marc Prud'hommeaux
54 * @nojavadoc
55 */
56 public class EntityManagerFactoryImpl
57 implements OpenJPAEntityManagerFactory, OpenJPAEntityManagerFactorySPI,
58 Closeable {
59
60 private static final Localizer _loc = Localizer.forPackage
61 (EntityManagerFactoryImpl.class);
62
63 private DelegatingBrokerFactory _factory = null;
64 private transient Constructor<FetchPlan> _plan = null;
65 private transient StoreCache _cache = null;
66 private transient QueryResultCache _queryCache = null;
67
68 /**
69 * Default constructor provided for auto-instantiation.
70 */
71 public EntityManagerFactoryImpl() {
72 }
73
74 /**
75 * Supply delegate on construction.
76 */
77 public EntityManagerFactoryImpl(BrokerFactory factory) {
78 setBrokerFactory(factory);
79 }
80
81 /**
82 * Delegate.
83 */
84 public BrokerFactory getBrokerFactory() {
85 return _factory.getDelegate();
86 }
87
88 /**
89 * Delegate must be provided before use.
90 */
91 public void setBrokerFactory(BrokerFactory factory) {
92 _factory = new DelegatingBrokerFactory(factory,
93 PersistenceExceptions.TRANSLATOR);
94 }
95
96 public OpenJPAConfiguration getConfiguration() {
97 return _factory.getConfiguration();
98 }
99
100 public Properties getProperties() {
101 return _factory.getProperties();
102 }
103
104 public Object putUserObject(Object key, Object val) {
105 return _factory.putUserObject(key, val);
106 }
107
108 public Object getUserObject(Object key) {
109 return _factory.getUserObject(key);
110 }
111
112 public StoreCache getStoreCache() {
113 _factory.lock();
114 try {
115 if (_cache == null) {
116 OpenJPAConfiguration conf = _factory.getConfiguration();
117 _cache = new StoreCacheImpl(this,
118 conf.getDataCacheManagerInstance().getSystemDataCache());
119 }
120 return _cache;
121 } finally {
122 _factory.unlock();
123 }
124 }
125
126 public StoreCache getStoreCache(String cacheName) {
127 return new StoreCacheImpl(this, _factory.getConfiguration().
128 getDataCacheManagerInstance().getDataCache(cacheName, true));
129 }
130
131 public QueryResultCache getQueryResultCache() {
132 _factory.lock();
133 try {
134 if (_queryCache == null)
135 _queryCache = new QueryResultCacheImpl(_factory.
136 getConfiguration().getDataCacheManagerInstance().
137 getSystemQueryCache());
138 return _queryCache;
139 } finally {
140 _factory.unlock();
141 }
142 }
143
144 public OpenJPAEntityManagerSPI createEntityManager() {
145 return createEntityManager(null);
146 }
147
148 public OpenJPAEntityManagerSPI createEntityManager(Map props) {
149 if (props == null)
150 props = Collections.EMPTY_MAP;
151 else if (!props.isEmpty())
152 props = new HashMap(props);
153
154 OpenJPAConfiguration conf = getConfiguration();
155 String user = (String) Configurations.removeProperty
156 ("ConnectionUserName", props);
157 if (user == null)
158 user = conf.getConnectionUserName();
159 String pass = (String) Configurations.removeProperty
160 ("ConnectionPassword", props);
161 if (pass == null)
162 pass = conf.getConnectionPassword();
163
164 String str = (String) Configurations.removeProperty
165 ("TransactionMode", props);
166 boolean managed;
167 if (str == null)
168 managed = conf.isTransactionModeManaged();
169 else {
170 Value val = conf.getValue("TransactionMode");
171 managed = Boolean.parseBoolean(val.unalias(str));
172 }
173
174 Object obj = Configurations.removeProperty("ConnectionRetainMode",
175 props);
176 int retainMode;
177 if (obj instanceof Number)
178 retainMode = ((Number) obj).intValue();
179 else if (obj == null)
180 retainMode = conf.getConnectionRetainModeConstant();
181 else {
182 Value val = conf.getValue("ConnectionRetainMode");
183 try {
184 retainMode = Integer.parseInt(val.unalias((String) obj));
185 } catch (Exception e) {
186 throw new ArgumentException(_loc.get("bad-em-prop",
187 "openjpa.ConnectionRetainMode", obj),
188 new Throwable[]{ e }, obj, true);
189 }
190 }
191
192 Broker broker = _factory.newBroker(user, pass, managed, retainMode,
193 false);
194
195 // add autodetach for close and rollback conditions to the configuration
196 broker.setAutoDetach(AutoDetach.DETACH_CLOSE, true);
197 broker.setAutoDetach(AutoDetach.DETACH_ROLLBACK, true);
198
199 broker.setDetachedNew(false);
200 OpenJPAEntityManagerSPI em = newEntityManagerImpl(broker);
201
202 // allow setting of other bean properties of EM
203 String[] prefixes = ProductDerivations.getConfigurationPrefixes();
204 List<RuntimeException> errs = null;
205 Method setter;
206 String prop, prefix;
207 Object val;
208 for (Map.Entry entry : (Set<Map.Entry>) props.entrySet()) {
209 prop = (String) entry.getKey();
210 prefix = null;
211 for (int i = 0; i < prefixes.length; i++) {
212 prefix = prefixes[i] + ".";
213 if (prop.startsWith(prefix))
214 break;
215 prefix = null;
216 }
217 if (prefix == null)
218 continue;
219 prop = prop.substring(prefix.length());
220 try {
221 setter = Reflection.findSetter(em.getClass(), prop, true);
222 } catch (OpenJPAException ke) {
223 if (errs == null)
224 errs = new LinkedList<RuntimeException>();
225 errs.add(PersistenceExceptions.toPersistenceException(ke));
226 continue;
227 }
228
229 val = entry.getValue();
230 try {
231 if (val instanceof String) {
232 if ("null".equals(val))
233 val = null;
234 else
235 val = Strings.parse((String) val,
236 setter.getParameterTypes()[0]);
237 }
238 Reflection.set(em, setter, val);
239 } catch (Throwable t) {
240 while (t.getCause() != null)
241 t = t.getCause();
242 ArgumentException err = new ArgumentException(_loc.get
243 ("bad-em-prop", prop, entry.getValue()),
244 new Throwable[]{ t }, null, true);
245 if (errs == null)
246 errs = new LinkedList<RuntimeException>();
247 errs.add(err);
248 }
249 }
250
251 if (errs != null) {
252 em.close();
253 if (errs.size() == 1)
254 throw errs.get(0);
255 throw new ArgumentException(_loc.get("bad-em-props"),
256 errs.toArray(new Throwable[errs.size()]),
257 null, true);
258 }
259 return em;
260 }
261
262 /**
263 * Create a new entity manager around the given broker.
264 */
265 protected EntityManagerImpl newEntityManagerImpl(Broker broker) {
266 return new EntityManagerImpl(this, broker);
267 }
268
269 public void addLifecycleListener(Object listener, Class... classes) {
270 _factory.addLifecycleListener(listener, classes);
271 }
272
273 public void removeLifecycleListener(Object listener) {
274 _factory.removeLifecycleListener(listener);
275 }
276
277 public void addTransactionListener(Object listener) {
278 _factory.addTransactionListener(listener);
279 }
280
281 public void removeTransactionListener(Object listener) {
282 _factory.removeTransactionListener(listener);
283 }
284
285 public void close() {
286 _factory.close();
287 }
288
289 public boolean isOpen() {
290 return !_factory.isClosed();
291 }
292
293 public int hashCode() {
294 return _factory.hashCode();
295 }
296
297 public boolean equals(Object other) {
298 if (other == this)
299 return true;
300 if (!(other instanceof EntityManagerFactoryImpl))
301 return false;
302 return _factory.equals(((EntityManagerFactoryImpl) other)._factory);
303 }
304
305 /**
306 * Create a store-specific facade for the given fetch configuration.
307 * If no facade class exists, we use the default {@link FetchPlan}.
308 */
309 FetchPlan toFetchPlan(Broker broker, FetchConfiguration fetch) {
310 if (fetch == null)
311 return null;
312
313 if (fetch instanceof DelegatingFetchConfiguration)
314 fetch = ((DelegatingFetchConfiguration) fetch).
315 getInnermostDelegate();
316
317 try {
318 if (_plan == null) {
319 Class storeType = (broker == null) ? null : broker.
320 getStoreManager().getInnermostDelegate().getClass();
321 Class cls = _factory.getConfiguration().
322 getStoreFacadeTypeRegistry().
323 getImplementation(FetchPlan.class, storeType,
324 FetchPlanImpl.class);
325 _plan = cls.getConstructor(FetchConfiguration.class);
326 }
327 return _plan.newInstance(fetch);
328 } catch (InvocationTargetException ite) {
329 throw PersistenceExceptions.toPersistenceException
330 (ite.getTargetException());
331 } catch (Exception e) {
332 throw PersistenceExceptions.toPersistenceException(e);
333 }
334 }
335 }