KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cayenne > jpa > Provider


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
20 package org.apache.cayenne.jpa;
21
22 import java.io.InputStream JavaDoc;
23 import java.io.PrintWriter JavaDoc;
24 import java.lang.instrument.ClassFileTransformer JavaDoc;
25 import java.sql.Connection JavaDoc;
26 import java.sql.ResultSet JavaDoc;
27 import java.sql.SQLException JavaDoc;
28 import java.util.Collection JavaDoc;
29 import java.util.Map JavaDoc;
30 import java.util.Properties JavaDoc;
31
32 import javax.persistence.EntityManagerFactory;
33 import javax.persistence.spi.PersistenceProvider;
34 import javax.persistence.spi.PersistenceUnitInfo;
35 import javax.persistence.spi.PersistenceUnitTransactionType;
36 import javax.sql.DataSource JavaDoc;
37
38 import org.apache.cayenne.access.DataDomain;
39 import org.apache.cayenne.access.DataNode;
40 import org.apache.cayenne.access.DbGenerator;
41 import org.apache.cayenne.conf.Configuration;
42 import org.apache.cayenne.conf.ConnectionProperties;
43 import org.apache.cayenne.dba.AutoAdapter;
44 import org.apache.cayenne.dba.DbAdapter;
45 import org.apache.cayenne.enhancer.Enhancer;
46 import org.apache.cayenne.jpa.bridge.DataMapConverter;
47 import org.apache.cayenne.jpa.conf.DefaultDataSourceFactory;
48 import org.apache.cayenne.jpa.conf.EntityMapLoader;
49 import org.apache.cayenne.jpa.conf.EntityMapLoaderContext;
50 import org.apache.cayenne.jpa.conf.UnitLoader;
51 import org.apache.cayenne.jpa.enhancer.JpaEnhancerVisitorFactory;
52 import org.apache.cayenne.jpa.instrument.UnitClassTransformer;
53 import org.apache.cayenne.jpa.map.JpaClassDescriptor;
54 import org.apache.cayenne.jpa.reflect.JpaClassDescriptorFactory;
55 import org.apache.cayenne.map.DataMap;
56 import org.apache.cayenne.map.DbEntity;
57 import org.apache.cayenne.reflect.ClassDescriptorMap;
58 import org.apache.cayenne.util.ResourceLocator;
59 import org.apache.cayenne.util.Util;
60 import org.apache.cayenne.validation.SimpleValidationFailure;
61 import org.apache.cayenne.validation.ValidationResult;
62 import org.apache.commons.logging.Log;
63 import org.apache.commons.logging.LogFactory;
64
65 /**
66  * A PersistenceProvider implementation based on Cayenne stack. Wraps a Cayenne
67  * Configuration instance.
68  *
69  * @author Andrus Adamchik
70  */

71 public class Provider implements PersistenceProvider {
72
73     // spec-defined properties per ch. 7.1.3.1.
74
public static final String JavaDoc PROVIDER_PROPERTY = "javax.persistence.provider";
75     public static final String JavaDoc TRANSACTION_TYPE_PROPERTY = "javax.persistence.transactionType";
76     public static final String JavaDoc JTA_DATA_SOURCE_PROPERTY = "javax.persistence.jtaDataSource";
77     public static final String JavaDoc NON_JTA_DATA_SOURCE_PROPERTY = "javax.persistence.nonJtaDataSource";
78
79     // provider-specific properties. Must use provider namespace per ch. 7.1.3.1.
80
public static final String JavaDoc CREATE_SCHEMA_PROPERTY = "org.apache.cayenne.schema.create";
81     public static final String JavaDoc DATA_SOURCE_FACTORY_PROPERTY = "org.apache.cayenne.jpa.jpaDataSourceFactory";
82
83     // ... DataSource
84
public static final String JavaDoc ADAPTER_PROPERTY = "org.apache.cayenne."
85             + ConnectionProperties.ADAPTER_KEY;
86     public static final String JavaDoc DATA_SOURCE_DRIVER_PROPERTY = "org.apache.cayenne.datasource."
87             + ConnectionProperties.DRIVER_KEY;
88     public static final String JavaDoc DATA_SOURCE_URL_PROPERTY = "org.apache.cayenne.datasource."
89             + ConnectionProperties.URL_KEY;
90     public static final String JavaDoc DATA_SOURCE_USER_NAME_PROPERTY = "org.apache.cayenne.datasource."
91             + ConnectionProperties.USER_NAME_KEY;
92     public static final String JavaDoc DATA_SOURCE_PASSWORD_PROPERTY = "org.apache.cayenne.datasource."
93             + ConnectionProperties.PASSWORD_KEY;
94     public static final String JavaDoc DATA_SOURCE_MIN_CONNECTIONS_PROPERTY = "org.apache.cayenne.datasource.jdbc.minConnections";
95     public static final String JavaDoc DATA_SOURCE_MAX_CONNECTIONS_PROPERTY = "org.apache.cayenne.datasource.jdbc.maxConnections";
96
97     protected boolean validateDescriptors;
98     protected UnitLoader unitLoader;
99     protected Properties JavaDoc defaultProperties;
100     protected Configuration configuration;
101     protected Log logger;
102
103     /**
104      * Creates a new PersistenceProvider with properties configured to run in a standalone
105      * mode with Cayenne stack.
106      */

107     public Provider() {
108         this(false);
109     }
110
111     public Provider(boolean validateDescriptors) {
112         this.validateDescriptors = validateDescriptors;
113         this.defaultProperties = new Properties JavaDoc();
114
115         configureEnvironmentProperties();
116         configureDefaultProperties();
117
118         this.logger = LogFactory.getLog(getClass());
119         this.configuration = new LazyConfiguration();
120
121         // set a singleton that may be used by Cayenne
122
Configuration.initializeSharedConfiguration(configuration);
123     }
124
125     /**
126      * Loads default properties from the Java environment.
127      */

128     protected void configureEnvironmentProperties() {
129         String JavaDoc dsFactory = System.getProperty(DATA_SOURCE_FACTORY_PROPERTY);
130         if (dsFactory != null) {
131             defaultProperties.put(DATA_SOURCE_FACTORY_PROPERTY, dsFactory);
132         }
133
134         String JavaDoc transactionType = System.getProperty(TRANSACTION_TYPE_PROPERTY);
135         if (transactionType != null) {
136             defaultProperties.put(TRANSACTION_TYPE_PROPERTY, transactionType);
137         }
138     }
139
140     protected void configureDefaultProperties() {
141         if (!defaultProperties.containsKey(DATA_SOURCE_FACTORY_PROPERTY)) {
142             defaultProperties.put(
143                     DATA_SOURCE_FACTORY_PROPERTY,
144                     DefaultDataSourceFactory.class.getName());
145         }
146
147         if (!defaultProperties.containsKey(TRANSACTION_TYPE_PROPERTY)) {
148             defaultProperties.put(
149                     TRANSACTION_TYPE_PROPERTY,
150                     PersistenceUnitTransactionType.RESOURCE_LOCAL.name());
151         }
152     }
153
154     /**
155      * Called by Persistence class when an EntityManagerFactory is to be created. Creates
156      * a {@link JpaUnit} and calls
157      * {@link #createContainerEntityManagerFactory(PersistenceUnitInfo, Map)}.
158      */

159     public EntityManagerFactory createEntityManagerFactory(String JavaDoc emName, Map JavaDoc map) {
160
161         JpaUnit ui = loadUnit(emName);
162
163         if (ui == null) {
164             return null;
165         }
166
167         // override properties
168
if (map != null) {
169             ui.addProperties(map);
170         }
171
172         // set default properties if they are not set explicitly
173
Properties JavaDoc properties = ui.getProperties();
174         for (Map.Entry JavaDoc property : defaultProperties.entrySet()) {
175             if (!properties.containsKey(property.getKey())) {
176                 properties.put(property.getKey(), property.getValue());
177             }
178         }
179
180         // check if we are allowed to handle this unit (JPA Spec, 7.2)
181
String JavaDoc provider = ui.getPersistenceProviderClassName();
182         if (provider != null && !provider.equals(this.getClass().getName())) {
183             return null;
184         }
185
186         // do not pass properties further down, they are already acounted for in the
187
// PersistenceUnitInfo.
188
return createContainerEntityManagerFactory(ui, null);
189     }
190
191     /**
192      * Called by the container when an EntityManagerFactory is to be created. Returns a
193      * {@link EntityManagerFactory} which is a DataDomain wrapper. Note that Cayenne
194      * provider will ignore all but 'javax.persistence.transactionType' property in the
195      * passed property map.
196      */

197     public synchronized EntityManagerFactory createContainerEntityManagerFactory(
198             PersistenceUnitInfo unit,
199             Map JavaDoc map) {
200
201         String JavaDoc name = unit.getPersistenceUnitName();
202         DataDomain domain = configuration.getDomain(name);
203
204         // TODO: andrus, 2/3/2007 - considering property overrides, it may be a bad idea
205
// to cache domains. Essentially we are caching a PersistenceUnitInfo with a given
206
// name, without a possibility to refresh it. But maybe this is ok...?
207
if (domain == null) {
208
209             long t0 = System.currentTimeMillis();
210
211             boolean isJTA = isJta(unit, map);
212
213             // configure Cayenne domain
214
domain = new DataDomain(name);
215             ClassDescriptorMap descriptors = domain
216                     .getEntityResolver()
217                     .getClassDescriptorMap();
218
219             descriptors.addFactory(new JpaClassDescriptorFactory(descriptors));
220             configuration.addDomain(domain);
221
222             EntityMapLoader loader = new EntityMapLoader(unit);
223
224             // add transformer before DataMapConverter starts loading the classes via app
225
// class loader
226
Map JavaDoc<String JavaDoc, JpaClassDescriptor> managedClasses = loader
227                     .getEntityMap()
228                     .getMangedClasses();
229             ClassFileTransformer JavaDoc enhancer = new Enhancer(new JpaEnhancerVisitorFactory(
230                     managedClasses));
231             unit.addTransformer(new UnitClassTransformer(managedClasses, loader
232                     .getContext()
233                     .getTempClassLoader(), enhancer));
234
235             DataMapConverter converter = new DataMapConverter();
236             DataMap cayenneMap = converter.toDataMap(name, loader.getContext());
237
238             // TODO: andrus, 2/3/2007 - clarify this logic.... JTA EM may not always mean
239
// JTA DS?
240
DataSource JavaDoc dataSource = isJTA ? unit.getJtaDataSource() : unit
241                     .getNonJtaDataSource();
242
243             DbAdapter adapter = createCustomAdapter(loader.getContext(), unit);
244             DataNode node = new DataNode(name);
245             if (adapter == null) {
246                 adapter = new AutoAdapter(new NodeDataSource(node));
247             }
248
249             node.setAdapter(adapter);
250             node.setDataSource(dataSource);
251             node.addDataMap(cayenneMap);
252
253             domain.addNode(node);
254             domain.setUsingExternalTransactions(isJTA);
255
256             if ("true".equalsIgnoreCase(unit.getProperties().getProperty(
257                     CREATE_SCHEMA_PROPERTY))) {
258                 loadSchema(dataSource, adapter, cayenneMap);
259             }
260
261             long t1 = System.currentTimeMillis();
262
263             // report conflicts...
264
ValidationResult conflicts = loader.getContext().getConflicts();
265             if (conflicts.hasFailures()) {
266                 for (Object JavaDoc failure : conflicts.getFailures()) {
267                     logger.info("*** mapping conflict: " + failure);
268                 }
269             }
270
271             if (logger.isDebugEnabled()) {
272                 logger.debug("loaded persistence unit '"
273                         + name
274                         + "' in "
275                         + (t1 - t0)
276                         + " ms.");
277             }
278         }
279
280         // see TODO above - JTA vs RESOURCE_LOCAL is cached per domain... maybe need to
281
// change that
282
return domain.isUsingExternalTransactions() ? new JtaEntityManagerFactory(
283                 this,
284                 domain,
285                 unit) : new ResourceLocalEntityManagerFactory(this, domain, unit);
286     }
287
288     /**
289      * Returns whether provided configuration specifies a JTA or RESOURCE_LOCAL
290      * EntityManager.
291      */

292     private boolean isJta(PersistenceUnitInfo unit, Map JavaDoc overrides) {
293         PersistenceUnitTransactionType txType;
294         String JavaDoc txTypeOverride = (overrides != null) ? (String JavaDoc) overrides
295                 .get(TRANSACTION_TYPE_PROPERTY) : null;
296         if (txTypeOverride != null) {
297             txType = PersistenceUnitTransactionType.valueOf(txTypeOverride);
298         }
299         else {
300             txType = unit.getTransactionType();
301         }
302
303         return txType == PersistenceUnitTransactionType.JTA;
304     }
305
306     /**
307      * Loads database schema if it doesn't yet exist.
308      */

309     protected void loadSchema(DataSource JavaDoc dataSource, DbAdapter adapter, DataMap map) {
310
311         Collection JavaDoc tables = map.getDbEntities();
312         if (tables.isEmpty()) {
313             return;
314         }
315
316         // sniff a first table precense
317

318         // TODO: andrus 9/1/2006 - should we make this check a part of DbGenerator (and
319
// query - a part of DbAdapter)?
320
DbEntity table = (DbEntity) tables.iterator().next();
321
322         try {
323             Connection JavaDoc c = dataSource.getConnection();
324             try {
325
326                 String JavaDoc tableName = table.getName().toLowerCase();
327
328                 // select all tables to avoid case sensitivity issues.
329
ResultSet JavaDoc rs = c.getMetaData().getTables(
330                         table.getCatalog(),
331                         table.getSchema(),
332                         null,
333                         null);
334
335                 try {
336                     while (rs.next()) {
337                         String JavaDoc sqlName = rs.getString("TABLE_NAME");
338                         if (tableName.equals(sqlName.toLowerCase())) {
339                             logger.debug("table "
340                                     + table.getFullyQualifiedName()
341                                     + " is present; will skip schema generation.");
342                             return;
343                         }
344                     }
345                 }
346                 finally {
347                     rs.close();
348                 }
349             }
350             finally {
351                 c.close();
352             }
353         }
354         catch (SQLException JavaDoc e1) {
355             // db exists
356
logger.debug("error generating schema, assuming schema exists.");
357             return;
358         }
359
360         logger.debug("table "
361                 + table.getFullyQualifiedName()
362                 + " is absent; will continue with schema generation.");
363
364         // run generator
365
DbGenerator generator = new DbGenerator(adapter, map);
366         try {
367             generator.runGenerator(dataSource);
368         }
369         catch (Exception JavaDoc e) {
370
371         }
372     }
373
374     protected DbAdapter createCustomAdapter(
375             EntityMapLoaderContext context,
376             PersistenceUnitInfo info) {
377
378         String JavaDoc adapterClass = info.getProperties().getProperty(ADAPTER_PROPERTY);
379
380         if (Util.isEmptyString(adapterClass)) {
381             return null;
382         }
383
384         try {
385             // adapter class is not enhanced, so use a normal class loader
386
Class JavaDoc dbAdapterClass = Class.forName(adapterClass, true, Thread
387                     .currentThread()
388                     .getContextClassLoader());
389             return (DbAdapter) dbAdapterClass.newInstance();
390         }
391         catch (Exception JavaDoc e) {
392             context.recordConflict(new SimpleValidationFailure(
393                     info,
394                     "Failed to load adapter '"
395                             + adapterClass
396                             + "', message: "
397                             + e.getLocalizedMessage()));
398             return null;
399         }
400     }
401
402     public Configuration getConfiguration() {
403         return configuration;
404     }
405
406     /**
407      * Loads a named JpaUnit using internal UnitLoader.
408      */

409     protected JpaUnit loadUnit(String JavaDoc emName) {
410         // TODO: Andrus, 2/11/2006 - cache loaded units (or factories)...?
411
return getUnitLoader().loadUnit(emName);
412     }
413
414     /**
415      * Returns unit loader, lazily creating it on first invocation.
416      */

417     protected UnitLoader getUnitLoader() {
418         if (unitLoader == null) {
419             this.unitLoader = new UnitLoader(validateDescriptors);
420         }
421
422         return unitLoader;
423     }
424
425     // TODO: andrus, 4/29/2006 - this is copied from non-public conf.NodeDataSource. In
426
// Cayenne > 1.2 make it public.
427
class NodeDataSource implements DataSource JavaDoc {
428
429         DataNode node;
430
431         NodeDataSource(DataNode node) {
432             this.node = node;
433         }
434
435         public Connection JavaDoc getConnection() throws SQLException JavaDoc {
436             return node.getDataSource().getConnection();
437         }
438
439         public Connection JavaDoc getConnection(String JavaDoc username, String JavaDoc password)
440                 throws SQLException JavaDoc {
441             return node.getDataSource().getConnection(username, password);
442         }
443
444         public PrintWriter JavaDoc getLogWriter() throws SQLException JavaDoc {
445             return node.getDataSource().getLogWriter();
446         }
447
448         public void setLogWriter(PrintWriter JavaDoc out) throws SQLException JavaDoc {
449             node.getDataSource().setLogWriter(out);
450         }
451
452         public void setLoginTimeout(int seconds) throws SQLException JavaDoc {
453             node.getDataSource().setLoginTimeout(seconds);
454         }
455
456         public int getLoginTimeout() throws SQLException JavaDoc {
457             return node.getDataSource().getLoginTimeout();
458         }
459     }
460
461     protected String JavaDoc getDefaultProperty(String JavaDoc key) {
462         return defaultProperties.getProperty(key);
463     }
464
465     class LazyConfiguration extends Configuration {
466
467         @Override JavaDoc
468         public boolean canInitialize() {
469             return true;
470         }
471
472         @Override JavaDoc
473         public void initialize() throws Exception JavaDoc {
474         }
475
476         @Override JavaDoc
477         public void didInitialize() {
478         }
479
480         @Override JavaDoc
481         protected ResourceLocator getResourceLocator() {
482             throw new UnsupportedOperationException JavaDoc();
483         }
484
485         @Override JavaDoc
486         protected InputStream JavaDoc getDomainConfiguration() {
487             throw new UnsupportedOperationException JavaDoc();
488         }
489
490         @Override JavaDoc
491         protected InputStream JavaDoc getMapConfiguration(String JavaDoc name) {
492             throw new UnsupportedOperationException JavaDoc();
493         }
494
495         @Override JavaDoc
496         protected InputStream JavaDoc getViewConfiguration(String JavaDoc location) {
497             throw new UnsupportedOperationException JavaDoc();
498         }
499     }
500 }
501
Popular Tags