1 package org.hibernate.ejb; 3 4 import java.io.DataInputStream ; 5 import java.io.IOException ; 6 import java.io.InputStream ; 7 import java.net.URL ; 8 import java.util.ArrayList ; 9 import java.util.Collection ; 10 import java.util.Enumeration ; 11 import java.util.Iterator ; 12 import java.util.List ; 13 import java.util.Map ; 14 import java.util.Properties ; 15 import java.util.Set ; 16 import java.util.StringTokenizer ; 17 import javax.persistence.Embeddable; 18 import javax.persistence.EmbeddableSuperclass; 19 import javax.persistence.Entity; 20 import javax.persistence.EntityManagerFactory; 21 import javax.persistence.PersistenceException; 22 import javax.persistence.spi.PersistenceInfo; 23 24 import javassist.ClassPool; 25 import javassist.CtClass; 26 import javassist.LoaderClassPath; 27 import javassist.NotFoundException; 28 import javassist.bytecode.AnnotationsAttribute; 29 import javassist.bytecode.ClassFile; 30 import org.apache.commons.logging.Log; 31 import org.apache.commons.logging.LogFactory; 32 import org.hibernate.MappingException; 33 import org.hibernate.SessionFactory; 34 import org.hibernate.event.SessionEventListenerConfig; 35 import org.hibernate.cfg.AnnotationConfiguration; 36 import org.hibernate.cfg.Environment; 37 import org.hibernate.ejb.callback.EJB3DeleteEventListener; 38 import org.hibernate.ejb.callback.EJB3MergeEventListener; 39 import org.hibernate.ejb.callback.EJB3PersistEventListener; 40 import org.hibernate.ejb.callback.EJB3PostDeleteEventListener; 41 import org.hibernate.ejb.callback.EJB3PostLoadEventListener; 42 import org.hibernate.ejb.callback.EJB3PostUpdateEventListener; 43 import org.hibernate.ejb.callback.EntityCallbackHandler; 44 import org.hibernate.ejb.callback.EJB3PostInsertEventListener; 45 import org.hibernate.ejb.callback.EJB3SaveEventListener; 46 import org.hibernate.ejb.callback.EJB3SaveOrUpdateEventListener; 47 import org.hibernate.ejb.callback.EJB3FlushEntityEventListener; 48 import org.hibernate.ejb.packaging.JarVisitor; 49 import org.hibernate.ejb.packaging.PersistenceMetadata; 50 import org.hibernate.ejb.packaging.PersistenceXmlLoader; 51 import org.hibernate.mapping.PersistentClass; 52 import org.hibernate.secure.JACCConfiguration; 53 import org.hibernate.util.StringHelper; 54 import org.jboss.util.file.ArchiveBrowser; 55 56 61 public class HibernatePersistence implements javax.persistence.spi.PersistenceProvider { 62 private static Log log = LogFactory.getLog( HibernatePersistence.class ); 63 64 67 public static final String AUTODETECTION = "hibernate.ejb.autodetection"; 68 72 public static final String CLASS_NAMES = "hibernate.ejb.classes"; 73 77 public static final String PACKAGE_NAMES = "hibernate.ejb.packages"; 78 81 public static final String CFG_FILE = "hibernate.ejb.cfgfile"; 82 87 public static final String CLASS_CACHE_PREFIX = "hibernate.ejb.classcache"; 88 93 public static final String COLLECTION_CACHE_PREFIX = "hibernate.ejb.collectioncache"; 94 97 public static final String HBXML_FILES = "hibernate.hbmxml.files"; 98 public static final String LOADED_CLASSES = "hibernate.ejb.loaded.classes"; 99 public static final String JACC_CONTEXT_ID = "hibernate.jacc.ctx.id"; 100 public static final String JACC_PREFIX = "hibernate.jacc"; 101 public static final String JACC_PRE_INSERT_LISTENER = "hibernate.listener.pre.insert"; 103 public static final String JACC_PRE_LOAD_LISTENER = "hibernate.listener.pre.load"; 104 public static final String JACC_PRE_UPDATE_LISTENER = "hibernate.listener.pre.update"; 105 public static final String JACC_PRE_DELETE_LISTENER = "hibernate.listener.pre.delete"; 106 107 108 111 private EntityManagerFactory createFactory(PersistenceMetadata metadata, Map overrides) { 112 log.debug( "Creating Factory: " + metadata.getName() ); 113 if ( metadata.getMappingFiles().size() > 0 ) throw new RuntimeException ( "<mapping-file not supported yet" ); 114 115 Properties copy = new Properties (); 116 if ( StringHelper.isNotEmpty( metadata.getJtaDatasource() ) ) { 117 copy.put( Environment.DATASOURCE, metadata.getJtaDatasource() ); 118 } 119 copy.putAll( metadata.getProps() ); 120 if ( metadata.getClasses().size() <= 0 && 121 !copy.containsKey( LOADED_CLASSES ) && 122 !copy.containsKey( CLASS_NAMES ) && 123 !copy.containsKey( HBXML_FILES ) && 124 !copy.containsKey( CFG_FILE ) ) { 125 throw new MappingException( "Neither classes found/declared nor hibernate.cfg.xml file available" ); 127 } 128 129 if ( !copy.containsKey( LOADED_CLASSES ) && !copy.containsKey( CLASS_NAMES ) ) { 131 if ( metadata.getClasses().size() > 0 ) { 132 copy.put( CLASS_NAMES, metadata.getClasses() ); 133 } 134 if ( metadata.getPackages().size() > 0 ) { 135 if ( copy.containsKey( PACKAGE_NAMES ) ) { 136 ( (Set <String >) copy.get( PACKAGE_NAMES ) ).addAll( metadata.getPackages() ); 137 } 138 else { 139 copy.put( PACKAGE_NAMES, metadata.getPackages() ); 140 } 141 } 142 if ( metadata.getHbmfiles().size() > 0 ) { 143 if ( copy.containsKey( HBXML_FILES ) ) { 144 ( (Set <InputStream >) copy.get( HBXML_FILES ) ).addAll( metadata.getHbmfiles() ); 145 } 146 else { 147 copy.put( HBXML_FILES, metadata.getHbmfiles() ); 148 } 149 } 150 } 151 if ( overrides != null ) copy.putAll( overrides ); 152 return createEntityManagerFactory( copy ); 153 } 154 155 164 public EntityManagerFactory createEntityManagerFactory(String emName, Map map) { 165 try { 166 Enumeration <URL > xmls = Thread.currentThread().getContextClassLoader().getResources( "META-INF/persistence.xml" ); 167 while ( xmls.hasMoreElements() ) { 168 URL url = xmls.nextElement(); 169 log.debug( "Analyse of persistence.xml: " + url ); 170 PersistenceMetadata metadata = PersistenceXmlLoader.deploy( url ); 171 boolean[] detectArtifacts = detectArtifacts( metadata.getProps(), map); 172 173 if ( metadata.getProvider() == null || metadata.getProvider().equals( this.getClass().getName() ) ) { 174 JarVisitor visitor = JarVisitor.getVisitor( url, detectArtifacts[0], detectArtifacts[1] ); 179 if ( metadata.getName() == null ) { 180 if ( log.isDebugEnabled() ) { 181 log.debug( "Defaulting entity factory name to " + visitor.getName() ); 182 } 183 metadata.setName( visitor.getName() ); 184 } 185 addMetadataFromVisitor( visitor, metadata ); 186 for ( String jarFile : metadata.getJarFiles() ) { 187 visitor = JarVisitor.getVisitor( jarFile, detectArtifacts[0], detectArtifacts[1] ); 188 addMetadataFromVisitor( visitor, metadata ); 191 } 192 if ( emName == null ) { 193 if ( xmls.hasMoreElements() ) { 194 throw new PersistenceException( "No name provided and several persistence units found"); 195 } 196 return createFactory( metadata, map ); 197 } 198 else if ( emName.equals( metadata.getName() ) ) { 199 return createFactory( metadata, map ); 200 } 201 } 202 } 203 return null; 204 } 205 catch (Exception e) { 206 throw new PersistenceException( e ); 207 } 208 } 209 210 private void addMetadataFromVisitor(JarVisitor visitor, PersistenceMetadata metadata) throws IOException , 211 NotFoundException { 212 ClassPool cp = new ClassPool(); ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); 215 cp.insertClassPath( new LoaderClassPath( contextClassLoader ) ); 216 Set <String > elements = visitor.getClassNames(); 217 for ( String className : elements ) { 218 CtClass jsClass = cp.get( className ); 219 List attributes = jsClass.getClassFile().getAttributes(); 220 Iterator attributeIterators = attributes.listIterator(); 221 while ( attributeIterators.hasNext() ) { 222 Object info = attributeIterators.next(); 223 if ( info instanceof AnnotationsAttribute ) { 224 AnnotationsAttribute ann = (AnnotationsAttribute) info; 225 if ( ann.getAnnotation( Entity.class.getName() ) != null 226 || ann.getAnnotation( Embeddable.class.getName() ) != null 227 || ann.getAnnotation( EmbeddableSuperclass.class.getName() ) != null ) { 228 if ( log.isDebugEnabled() ) { 229 log.debug( "Add Javassist mapped class to " + metadata.getName() + ": " + className ); 230 } 231 metadata.getClasses().add( className ); 232 } 233 } 234 } 235 jsClass.detach(); } 237 elements = visitor.getPackageNames(); 238 for (String packageName : elements) { 239 metadata.getPackages().add( packageName ); 240 } 241 242 elements = visitor.getHbmFiles(); 243 for (String hbmFilesName : elements) { 244 log.debug("Trying to get resuource: " + "/" + hbmFilesName); 245 InputStream is = contextClassLoader.getResourceAsStream( hbmFilesName ); 246 if (is == null) throw new PersistenceException("Unable to load resource " + hbmFilesName); 247 metadata.getHbmfiles().add( is ); 248 } 249 } 250 251 254 public EntityManagerFactory createContainerEntityManagerFactory(PersistenceInfo info) { 255 List <String > entities = info.getEntityclassNames(); 256 if ( entities == null ) entities = new ArrayList <String >(); 257 List <InputStream > hbmFiles = new ArrayList <InputStream >(); 258 List <String > packages = new ArrayList <String >(); 259 boolean[] detectArtifact = detectArtifacts( info.getProperties(), null ); 260 for ( URL jar : info.getJarFiles() ) { 261 if (detectArtifact[0]) scanForClasses( jar, packages, entities ); 262 if (detectArtifact[1]) scanForHbmXmlFiles( jar, hbmFiles ); 263 } 264 Properties props = info.getProperties(); 265 if ( props == null ) props = new Properties (); 266 props.put( CLASS_NAMES, entities ); 267 props.put( PACKAGE_NAMES, packages ); 268 if ( hbmFiles.size() > 0 ) props.put( HBXML_FILES, hbmFiles ); 269 Ejb3Configuration cfg = new Ejb3Configuration(); 270 if ( info.getJtaDataSource() != null || info.getNonJtaDataSource() != null ) { 271 cfg.setProperties(props); 272 cfg.setDataSource( info.getJtaDataSource() != null ? info.getJtaDataSource() : info.getNonJtaDataSource() ); 273 props.setProperty( Environment.CONNECTION_PROVIDER, InjectedDataSourceConnectionProvider.class.getName() ); 274 } 275 else { 276 throw new PersistenceException("No datasource provided"); 278 } 279 return createEntityManagerFactory( props, cfg ); 280 281 } 282 283 private boolean[] detectArtifacts(Properties properties, Map overridenProperties) { 284 boolean[] result = new boolean[2]; 285 result[0] = false; result[1] = false; if ( (overridenProperties != null 288 && overridenProperties.get(CFG_FILE) != null ) 289 || properties.get( CFG_FILE ) != null ) { 290 log.info( "Avoid .par auto detection: cfg file defined" ); 291 return result; 292 } 293 String detect = overridenProperties != null ? 294 (String ) overridenProperties.get(AUTODETECTION) : 295 null; 296 detect = detect == null ? 297 properties.getProperty(AUTODETECTION, "class,hbm") : 298 detect; 299 StringTokenizer st = new StringTokenizer ( detect, ",", false); 300 while ( st.hasMoreElements() ) { 301 String element = (String ) st.nextElement(); 302 if ( "class".equalsIgnoreCase( element ) ) result[0] = true; 303 if ( "hbm".equalsIgnoreCase( element ) ) result[1] = true; 304 } 305 log.debug( "Detect class: " + result[0] + "; detect hbm: " + result[1]); 306 return result; 307 } 308 309 private void scanForHbmXmlFiles(URL jar, List <InputStream > hbmxmls) { 310 Iterator it = ArchiveBrowser.getBrowser( 311 jar, new ArchiveBrowser.Filter() { 312 public boolean accept(String filename) { 313 return filename.endsWith( ".hbm.xml" ); 314 } 315 } 316 ); 317 318 while ( it.hasNext() ) { 319 InputStream stream = (InputStream ) it.next(); 320 hbmxmls.add( stream ); 321 } 322 } 323 324 private void scanForClasses(URL jar, List <String > packages, List <String > entities) { 325 Iterator it = ArchiveBrowser.getBrowser( 326 jar, new ArchiveBrowser.Filter() { 327 public boolean accept(String filename) { 328 return filename.endsWith( ".class" ); 329 } 330 } 331 ); 332 333 while ( it.hasNext() ) { 336 InputStream stream = (InputStream ) it.next(); 337 DataInputStream dstream = new DataInputStream ( stream ); 338 ClassFile cf = null; 339 try { 340 try { 341 cf = new ClassFile( dstream ); 342 } 343 finally { 344 dstream.close(); 345 stream.close(); 346 } 347 } 348 catch (IOException e) { 349 throw new RuntimeException ( e ); 350 } 351 if ( cf.getName().endsWith( ".package-info" ) ) { 352 int idx = cf.getName().indexOf( ".package-info" ); 353 String pkgName = cf.getName().substring( 0, idx ); 354 log.info( "found package: " + pkgName ); 355 packages.add( pkgName ); 356 continue; 357 } 358 359 AnnotationsAttribute visible = (AnnotationsAttribute) cf.getAttribute( AnnotationsAttribute.visibleTag ); 360 if ( visible != null ) { 361 boolean isEntity = visible.getAnnotation( Entity.class.getName() ) != null; 362 if ( isEntity ) { 363 log.info( "found EJB3 Entity bean: " + cf.getName() ); 364 entities.add( cf.getName() ); 365 } 366 boolean isEmbeddable = visible.getAnnotation( Embeddable.class.getName() ) != null; 367 if ( isEmbeddable ) { 368 log.info( "found EJB3 @Embeddable: " + cf.getName() ); 369 entities.add( cf.getName() ); 370 } 371 boolean isEmbeddableSuperclass = visible.getAnnotation( EmbeddableSuperclass.class.getName() ) != null; 372 if ( isEmbeddableSuperclass ) { 373 log.info( "found EJB3 @EmbeddableSuperclass: " + cf.getName() ); 374 entities.add( cf.getName() ); 375 } 376 } 377 } 378 } 379 380 383 public EntityManagerFactory createEntityManagerFactory(Map properties) { 385 return createEntityManagerFactory( properties, new AnnotationConfiguration() ); 386 } 387 388 391 private EntityManagerFactory createEntityManagerFactory(Map properties, AnnotationConfiguration cfg) { 392 cfg.getSessionEventListenerConfig().setFlushEventListener( EJB3FlushEventListener.INSTANCE ); 394 cfg.getSessionEventListenerConfig().setAutoFlushEventListener( EJB3AutoFlushEventListener.INSTANCE ); 395 396 Properties props = new Properties (); 397 398 props.setProperty( Environment.RELEASE_CONNECTIONS, "auto" ); 400 401 props.putAll( properties ); 404 405 cfg.setProperties( props ); 406 407 if ( properties == null || 408 !( properties.containsKey( CLASS_NAMES ) || 409 properties.containsKey( LOADED_CLASSES ) || 410 properties.containsKey( HBXML_FILES ) ) ) { 411 if ( properties != null && properties.containsKey( CFG_FILE ) ) { 413 cfg.configure( (String ) properties.get( CFG_FILE ) ); 414 } 415 else { 416 throw new MappingException( "Neither classes found/declared nor hibernate.cfg.xml file available" ); 417 } 418 } 419 else { 420 if ( properties.containsKey( CLASS_NAMES ) ) { 421 Collection <String > classNames = (Collection <String >) properties.get( CLASS_NAMES ); 422 addNamedAnnotatedClasses( cfg, classNames ); 423 } 424 if ( properties.containsKey( LOADED_CLASSES ) ) { 425 List <Class > classes = (List <Class >) properties.get( LOADED_CLASSES ); 426 for ( Class clazz : classes ) { 427 cfg.addAnnotatedClass( clazz ); 428 } 429 } 430 if ( properties.containsKey( PACKAGE_NAMES ) ) { 431 List <String > packages = (List <String >) properties.get( PACKAGE_NAMES ); 432 for ( String pkg : packages ) { 433 cfg.addPackage( pkg ); 434 } 435 } 436 if ( properties.containsKey( HBXML_FILES ) ) { 437 List <InputStream > hbmXmlFiles = (List <InputStream >) properties.get( HBXML_FILES ); 438 for ( InputStream is : hbmXmlFiles ) { 439 cfg.addInputStream( is ); 440 } 441 } 442 } 443 444 445 List <String > jaccKeys = new ArrayList <String >(); 446 String jaccContextId = (String ) properties.get( JACC_CONTEXT_ID ); 447 448 Iterator <String > propertyIt = properties.keySet().iterator(); 449 while ( propertyIt.hasNext() ) { 450 String propertyKey = propertyIt.next(); 451 if ( propertyKey.startsWith( CLASS_CACHE_PREFIX ) ) { 452 setCacheStrategy( propertyKey, properties, cfg, true ); 453 } 454 else if ( propertyKey.startsWith( COLLECTION_CACHE_PREFIX ) ) { 455 setCacheStrategy( propertyKey, properties, cfg, false ); 456 } 457 else if ( propertyKey.startsWith( JACC_PREFIX ) && !propertyKey.equals( JACC_CONTEXT_ID ) ) { 458 jaccKeys.add( propertyKey ); 459 } 460 } 461 462 if ( jaccKeys.size() > 0 ) { 463 addSecurity( jaccKeys, properties, cfg ); 464 } 465 466 cfg.setProperty( Environment.AUTOCOMMIT, "true" ); 468 cfg.setProperty( Environment.USE_IDENTIFIER_ROLLBACK, "true" ); 469 470 471 handleListenerCallbacks( cfg ); 472 SessionFactory sf = cfg.buildSessionFactory(); 473 474 return new EntityManagerFactoryImpl( sf ); 475 } 476 477 private void handleListenerCallbacks(AnnotationConfiguration cfg) { 478 EntityCallbackHandler callbackHandler = new EntityCallbackHandler(); 481 cfg.buildMappings(); Iterator classes = cfg.getClassMappings(); 483 while ( classes.hasNext() ) { 484 PersistentClass clazz = (PersistentClass) classes.next(); 485 callbackHandler.add( clazz.getMappedClass() ); 486 } 487 SessionEventListenerConfig listenerConfig = cfg.getSessionEventListenerConfig(); 489 490 listenerConfig.setDeleteEventListener( new EJB3DeleteEventListener( callbackHandler ) ); 492 listenerConfig.setFlushEntityEventListener( new EJB3FlushEntityEventListener( callbackHandler ) ); 493 listenerConfig.setMergeEventListener( new EJB3MergeEventListener( callbackHandler ) ); 494 listenerConfig.setCreateEventListener( new EJB3PersistEventListener( callbackHandler ) ); 495 listenerConfig.setPostDeleteEventListener( new EJB3PostDeleteEventListener( callbackHandler ) ); 496 listenerConfig.setPostInsertEventListener( new EJB3PostInsertEventListener( callbackHandler ) ); 497 listenerConfig.setPostLoadEventListener( new EJB3PostLoadEventListener( callbackHandler ) ); 498 listenerConfig.setPostUpdateEventListener( new EJB3PostUpdateEventListener( callbackHandler ) ); 499 listenerConfig.setSaveEventListener( new EJB3SaveEventListener( callbackHandler ) ); 500 listenerConfig.setSaveOrUpdateEventListener( new EJB3SaveOrUpdateEventListener( callbackHandler ) ); 501 } 502 503 private void setCacheStrategy(String propertyKey, Map properties, AnnotationConfiguration cfg, boolean isClass) { 504 String role = propertyKey.substring( 505 ( isClass ? CLASS_CACHE_PREFIX.length() : COLLECTION_CACHE_PREFIX.length() ) 506 + 1 507 ); 508 String value = (String ) properties.get( propertyKey ); 510 StringTokenizer params = new StringTokenizer ( value, ";, " ); 511 if ( !params.hasMoreTokens() ) { 512 StringBuffer error = new StringBuffer ( "Illegal usage of " ); 513 error.append( isClass ? CLASS_CACHE_PREFIX : COLLECTION_CACHE_PREFIX ); 514 error.append( ": " ).append( propertyKey ).append( " " ).append( value ); 515 throw new MappingException( error.toString() ); 516 } 517 String usage = params.nextToken(); 518 String region = null; 519 if ( params.hasMoreTokens() ) { 520 region = params.nextToken(); 521 } 522 if ( isClass ) { 523 cfg.setCacheConcurrencyStrategy( role, usage, region ); 524 } 525 else { 526 cfg.setCollectionCacheConcurrencyStrategy( role, usage, region ); 527 } 528 } 529 530 private static void addSecurity(List <String > keys, Map properties, AnnotationConfiguration cfg) { 531 log.debug( "Adding security" ); 532 if ( !properties.containsKey( JACC_CONTEXT_ID ) ) { 533 throw new MappingException( 534 "Entities have been configured for JACC, but " + JACC_CONTEXT_ID + " has not been set" 535 ); 536 } 537 String contextId = (String ) properties.get( JACC_CONTEXT_ID ); 538 539 541 if ( properties.containsKey( JACC_PRE_INSERT_LISTENER ) ) { 542 String listener = (String ) properties.get( JACC_PRE_INSERT_LISTENER ); 543 cfg.setListener( "pre-insert", listener ); 544 } 545 546 if ( properties.containsKey( JACC_PRE_UPDATE_LISTENER ) ) { 547 String listener = (String ) properties.get( JACC_PRE_UPDATE_LISTENER ); 548 cfg.setListener( "pre-update", listener ); 549 } 550 551 if ( properties.containsKey( JACC_PRE_DELETE_LISTENER ) ) { 552 String listener = (String ) properties.get( JACC_PRE_DELETE_LISTENER ); 553 cfg.setListener( "pre-delete", listener ); 554 } 555 556 if ( properties.containsKey( JACC_PRE_LOAD_LISTENER ) ) { 557 String listener = (String ) properties.get( JACC_PRE_LOAD_LISTENER ); 558 cfg.setListener( "pre-load", listener ); 559 } 560 561 int roleStart = JACC_PREFIX.length() + 1; 562 563 for ( String key : keys ) { 564 JACCConfiguration jaccCfg = new JACCConfiguration( contextId ); 565 566 try { 567 String role = key.substring( roleStart, key.indexOf( '.', roleStart ) ); 568 int classStart = roleStart + role.length() + 1; 569 String clazz = key.substring( classStart, key.length() ); 570 String actions = (String ) properties.get( key ); 571 jaccCfg.addPermission( role, clazz, actions ); 572 } 573 catch (IndexOutOfBoundsException e) { 574 throw new MappingException( "Illegal usage of " + JACC_PREFIX + ": " + key ); 575 } 576 } 577 } 578 579 private void addNamedAnnotatedClasses(AnnotationConfiguration cfg, Collection <String > classNames) { 580 for ( String name : classNames ) { 581 try { 582 Class clazz = Thread.currentThread().getContextClassLoader().loadClass( name ); 583 cfg.addAnnotatedClass( clazz ); 584 } 586 catch (ClassNotFoundException cnfe) { 587 Package pkg = Package.getPackage( name ); 588 if ( pkg == null ) { 589 throw new IllegalArgumentException ( "class not found", cnfe ); 590 } 591 else { 592 cfg.addPackage( name ); 593 } 594 } 595 } 596 } 597 598 } | Popular Tags |