1 19 20 package org.apache.cayenne.jpa; 21 22 import java.io.InputStream ; 23 import java.io.PrintWriter ; 24 import java.lang.instrument.ClassFileTransformer ; 25 import java.sql.Connection ; 26 import java.sql.ResultSet ; 27 import java.sql.SQLException ; 28 import java.util.Collection ; 29 import java.util.Map ; 30 import java.util.Properties ; 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 ; 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 71 public class Provider implements PersistenceProvider { 72 73 public static final String PROVIDER_PROPERTY = "javax.persistence.provider"; 75 public static final String TRANSACTION_TYPE_PROPERTY = "javax.persistence.transactionType"; 76 public static final String JTA_DATA_SOURCE_PROPERTY = "javax.persistence.jtaDataSource"; 77 public static final String NON_JTA_DATA_SOURCE_PROPERTY = "javax.persistence.nonJtaDataSource"; 78 79 public static final String CREATE_SCHEMA_PROPERTY = "org.apache.cayenne.schema.create"; 81 public static final String DATA_SOURCE_FACTORY_PROPERTY = "org.apache.cayenne.jpa.jpaDataSourceFactory"; 82 83 public static final String ADAPTER_PROPERTY = "org.apache.cayenne." 85 + ConnectionProperties.ADAPTER_KEY; 86 public static final String DATA_SOURCE_DRIVER_PROPERTY = "org.apache.cayenne.datasource." 87 + ConnectionProperties.DRIVER_KEY; 88 public static final String DATA_SOURCE_URL_PROPERTY = "org.apache.cayenne.datasource." 89 + ConnectionProperties.URL_KEY; 90 public static final String DATA_SOURCE_USER_NAME_PROPERTY = "org.apache.cayenne.datasource." 91 + ConnectionProperties.USER_NAME_KEY; 92 public static final String DATA_SOURCE_PASSWORD_PROPERTY = "org.apache.cayenne.datasource." 93 + ConnectionProperties.PASSWORD_KEY; 94 public static final String DATA_SOURCE_MIN_CONNECTIONS_PROPERTY = "org.apache.cayenne.datasource.jdbc.minConnections"; 95 public static final String DATA_SOURCE_MAX_CONNECTIONS_PROPERTY = "org.apache.cayenne.datasource.jdbc.maxConnections"; 96 97 protected boolean validateDescriptors; 98 protected UnitLoader unitLoader; 99 protected Properties defaultProperties; 100 protected Configuration configuration; 101 protected Log logger; 102 103 107 public Provider() { 108 this(false); 109 } 110 111 public Provider(boolean validateDescriptors) { 112 this.validateDescriptors = validateDescriptors; 113 this.defaultProperties = new Properties (); 114 115 configureEnvironmentProperties(); 116 configureDefaultProperties(); 117 118 this.logger = LogFactory.getLog(getClass()); 119 this.configuration = new LazyConfiguration(); 120 121 Configuration.initializeSharedConfiguration(configuration); 123 } 124 125 128 protected void configureEnvironmentProperties() { 129 String dsFactory = System.getProperty(DATA_SOURCE_FACTORY_PROPERTY); 130 if (dsFactory != null) { 131 defaultProperties.put(DATA_SOURCE_FACTORY_PROPERTY, dsFactory); 132 } 133 134 String 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 159 public EntityManagerFactory createEntityManagerFactory(String emName, Map map) { 160 161 JpaUnit ui = loadUnit(emName); 162 163 if (ui == null) { 164 return null; 165 } 166 167 if (map != null) { 169 ui.addProperties(map); 170 } 171 172 Properties properties = ui.getProperties(); 174 for (Map.Entry property : defaultProperties.entrySet()) { 175 if (!properties.containsKey(property.getKey())) { 176 properties.put(property.getKey(), property.getValue()); 177 } 178 } 179 180 String provider = ui.getPersistenceProviderClassName(); 182 if (provider != null && !provider.equals(this.getClass().getName())) { 183 return null; 184 } 185 186 return createContainerEntityManagerFactory(ui, null); 189 } 190 191 197 public synchronized EntityManagerFactory createContainerEntityManagerFactory( 198 PersistenceUnitInfo unit, 199 Map map) { 200 201 String name = unit.getPersistenceUnitName(); 202 DataDomain domain = configuration.getDomain(name); 203 204 if (domain == null) { 208 209 long t0 = System.currentTimeMillis(); 210 211 boolean isJTA = isJta(unit, map); 212 213 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 Map <String , JpaClassDescriptor> managedClasses = loader 227 .getEntityMap() 228 .getMangedClasses(); 229 ClassFileTransformer 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 DataSource 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 ValidationResult conflicts = loader.getContext().getConflicts(); 265 if (conflicts.hasFailures()) { 266 for (Object 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 return domain.isUsingExternalTransactions() ? new JtaEntityManagerFactory( 283 this, 284 domain, 285 unit) : new ResourceLocalEntityManagerFactory(this, domain, unit); 286 } 287 288 292 private boolean isJta(PersistenceUnitInfo unit, Map overrides) { 293 PersistenceUnitTransactionType txType; 294 String txTypeOverride = (overrides != null) ? (String ) 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 309 protected void loadSchema(DataSource dataSource, DbAdapter adapter, DataMap map) { 310 311 Collection tables = map.getDbEntities(); 312 if (tables.isEmpty()) { 313 return; 314 } 315 316 318 DbEntity table = (DbEntity) tables.iterator().next(); 321 322 try { 323 Connection c = dataSource.getConnection(); 324 try { 325 326 String tableName = table.getName().toLowerCase(); 327 328 ResultSet rs = c.getMetaData().getTables( 330 table.getCatalog(), 331 table.getSchema(), 332 null, 333 null); 334 335 try { 336 while (rs.next()) { 337 String 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 e1) { 355 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 DbGenerator generator = new DbGenerator(adapter, map); 366 try { 367 generator.runGenerator(dataSource); 368 } 369 catch (Exception e) { 370 371 } 372 } 373 374 protected DbAdapter createCustomAdapter( 375 EntityMapLoaderContext context, 376 PersistenceUnitInfo info) { 377 378 String adapterClass = info.getProperties().getProperty(ADAPTER_PROPERTY); 379 380 if (Util.isEmptyString(adapterClass)) { 381 return null; 382 } 383 384 try { 385 Class dbAdapterClass = Class.forName(adapterClass, true, Thread 387 .currentThread() 388 .getContextClassLoader()); 389 return (DbAdapter) dbAdapterClass.newInstance(); 390 } 391 catch (Exception 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 409 protected JpaUnit loadUnit(String emName) { 410 return getUnitLoader().loadUnit(emName); 412 } 413 414 417 protected UnitLoader getUnitLoader() { 418 if (unitLoader == null) { 419 this.unitLoader = new UnitLoader(validateDescriptors); 420 } 421 422 return unitLoader; 423 } 424 425 class NodeDataSource implements DataSource { 428 429 DataNode node; 430 431 NodeDataSource(DataNode node) { 432 this.node = node; 433 } 434 435 public Connection getConnection() throws SQLException { 436 return node.getDataSource().getConnection(); 437 } 438 439 public Connection getConnection(String username, String password) 440 throws SQLException { 441 return node.getDataSource().getConnection(username, password); 442 } 443 444 public PrintWriter getLogWriter() throws SQLException { 445 return node.getDataSource().getLogWriter(); 446 } 447 448 public void setLogWriter(PrintWriter out) throws SQLException { 449 node.getDataSource().setLogWriter(out); 450 } 451 452 public void setLoginTimeout(int seconds) throws SQLException { 453 node.getDataSource().setLoginTimeout(seconds); 454 } 455 456 public int getLoginTimeout() throws SQLException { 457 return node.getDataSource().getLoginTimeout(); 458 } 459 } 460 461 protected String getDefaultProperty(String key) { 462 return defaultProperties.getProperty(key); 463 } 464 465 class LazyConfiguration extends Configuration { 466 467 @Override 468 public boolean canInitialize() { 469 return true; 470 } 471 472 @Override 473 public void initialize() throws Exception { 474 } 475 476 @Override 477 public void didInitialize() { 478 } 479 480 @Override 481 protected ResourceLocator getResourceLocator() { 482 throw new UnsupportedOperationException (); 483 } 484 485 @Override 486 protected InputStream getDomainConfiguration() { 487 throw new UnsupportedOperationException (); 488 } 489 490 @Override 491 protected InputStream getMapConfiguration(String name) { 492 throw new UnsupportedOperationException (); 493 } 494 495 @Override 496 protected InputStream getViewConfiguration(String location) { 497 throw new UnsupportedOperationException (); 498 } 499 } 500 } 501 | Popular Tags |