This Bugzilla instance is deprecated, and most Eclipse projects now use GitHub or Eclipse GitLab. Please see the deprecation plan for details.
Bug 297928 - JPA2: EntityManagerFactoryImpl.getCriteriaBuilder() requires EM validation check for invalid state before attempting a getMetamodel()
Summary: JPA2: EntityManagerFactoryImpl.getCriteriaBuilder() requires EM validation ch...
Status: RESOLVED FIXED
Alias: None
Product: z_Archived
Classification: Eclipse Foundation
Component: Eclipselink (show other bugs)
Version: unspecified   Edit
Hardware: PC Windows 7
: P2 major with 1 vote (vote)
Target Milestone: ---   Edit
Assignee: Michael OBrien CLA
QA Contact:
URL: http://wiki.eclipse.org/EclipseLink/D...
Whiteboard:
Keywords:
Depends on: 266912 282518 322585
Blocks: 322184 303063 303684 322166
  Show dependency tree
 
Reported: 2009-12-16 02:34 EST by Michael OBrien CLA
Modified: 2022-06-09 10:10 EDT (History)
2 users (show)

See Also:
michael.f.obrien: documentation+


Attachments
Do a getServerSession that will do a login/deploy as part of an entityManagerFactory.predeploy() so all metadata processing, ddlgeneration and metamodel initialization is complete before a create em (5.51 KB, patch)
2010-08-17 22:19 EDT, Michael OBrien CLA
no flags Details | Diff
Option 7 (in use): Do a getServerSession that does a deploy/login that calls initializeDescriptors only for early emf.getMetamodel() users - em.getMetamodel() users do not login (3.28 KB, patch)
2010-09-03 10:46 EDT, Michael OBrien CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Michael OBrien CLA 2009-12-16 02:34:08 EST
>handle a possibly manually generated "_" canonical class that is missing imports to referenced entities

   1) Looks like your manually generated "Status_.java" canonical class is missing an import reference to "Status_.java" - this would cause generation of the javaType for the "status" OneToOne to fail - however the NPE needs to be fixed and replaced with a warning that the canonical model is incomplete.
    Try generating your "_" classes with the command in 2) below, or use the dynamic non-canonical ones created in memory during deployment.

For...

public static volatile SingularAttribute<Usuario, Status> status;

Add just after your package declaration...

import your.package.Status;

       As a result of this, the javaType of "Status" is not set on the "status" SingularAttribute.
       The fix will be to reference your other Status class.
       However, the NPE in this case needs to be addressed by keeping the superType as null == root level object
       I have raised bug # to fix this in 2.0.1

        // Assign all superType fields on all IdentifiableTypes (only after all managedType objects have been created)
        for(ManagedTypeImpl<?> potentialIdentifiableType : managedTypes.values()) {
            Class aClass = potentialIdentifiableType.getJavaType();
            /**
             * The superclass for top-level types is Object - however we set [null] as the supertype for root types.
             * 1) We are constrained by the fact that the spec requires that a superType be an IdentifiableType.
             *    Since [Object] is not an Entity or MappedSuperclass - it fails this criteria - as it would be a BasicType
             *    because it has no @Entity or @MappedSuperclass annotation.<p>
             * 2) Another object space reasoning issue behind this is to separate the Java and Metamodel object spaces.
             * In Java all types inherit from Object, however in the JPA Metamodel all types DO NOT inherit from a common type.
             * Therefore in the metamodel top-level root types have a superType of null.
             * See design issue discussion:
             * http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#DI_42:_20090709:_IdentifiableType.supertype_-_what_do_top-level_types_set_it_to
             */
370-->  Class superclass = aClass.getSuperclass();


    2) You can generate the "_" canonical metamodel classes using the following line, eclipse integration is in progress.
http://wiki.eclipse.org/UserGuide/JPA/Using_the_Canonical_Model_Generator_%28ELUG%29
    Here is an example generation of "_" java files without corresponding compiled class files (-proc:only) - placed at the cmd root
    Depending on your IDE, you will be able to automatically generate these as long as the services file is in your classpath.

javac -processor org.eclipse.persistence.internal.jpa.modelgen.CanonicalModelProcessor -proc:only -classpath lib/eclipselink.jar;lib/javax.persistence_2.0_preview.jar;punit src/org/eclipse/persistence/testing/models/jpa/metamodel/*.java


    thank you
    /michael
  
Diego Coronel wrote:
> Hi, 
>
>  Im trying JPA2 using glassfish V3 final, Weld, and im getting this error: 
>
> Caused by: java.lang.NullPointerException 
>         at
> org.eclipse.persistence.internal.jpa.metamodel.MetamodelImpl.initialize(MetamodelImpl.java:370) 
>         at
> org.eclipse.persistence.internal.jpa.metamodel.MetamodelImpl.<init>(MetamodelImpl.java:101) 
>         at
> org.eclipse.persistence.internal.jpa.metamodel.MetamodelImpl.<init>(MetamodelImpl.java:120) 
>         at
> org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.getMetamodel(EntityManagerSetupImpl.java:1939) 
>         at
> org.eclipse.persistence.internal.jpa.EntityManagerFactoryImpl.getMetamodel(EntityManagerFactoryImpl.java:472) 
>         at
> org.eclipse.persistence.internal.jpa.EntityManagerFactoryImpl.getCriteriaBuilder(EntityManagerFactoryImpl.java:456) 
>         at
> com.sun.enterprise.container.common.impl.EntityManagerFactoryWrapper.getCriteriaBuilder(EntityManagerFactoryWrapper.java:109) 
>         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
>         at
> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 
>         at
> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 
>         at java.lang.reflect.Method.invoke(Method.java:597) 
>         at
> org.jboss.weld.bean.proxy.ClientProxyMethodHandler.invoke(ClientProxyMethodHandler.java:113) 
>         at
> javax.persistence.EntityManagerFactory_$$_javassist_32.getCriteriaBuilder(EntityManagerFactory_$$_javassist_32.java) 
>         at
> com.fpf.model.security.dao.UsuarioDAO.getUsuario(UsuarioDAO.java:28) 
>         at
> com.fpf.web.security.login.Authenticator.isLogged(Authenticator.java:48) 
>         at
> com.fpf.web.security.login.Authenticator.authenticate(Authenticator.java:37) 
>         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
>         at
> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 
>         at
> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 
>         at java.lang.reflect.Method.invoke(Method.java:597) 
>         at
> org.jboss.weld.bean.proxy.ClientProxyMethodHandler.invoke(ClientProxyMethodHandler.java:113) 
>         at
> com.fpf.web.security.login.Authenticator_$$_javassist_31.authenticate(Authenticator_$$_javassist_31.java) 
>         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
>         at
> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 
>         at
> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 
>         at java.lang.reflect.Method.invoke(Method.java:597) 
>         at com.sun.el.parser.AstValue.invoke(AstValue.java:234) 
>         at
> com.sun.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:297) 
>         at
> org.jboss.weld.util.el.ForwardingMethodExpression.invoke(ForwardingMethodExpression.java:43) 
>         at
> org.jboss.weld.el.WeldMethodExpression.invoke(WeldMethodExpression.java:72) 
>         at
> com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:98) 
>         at
> javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:88) 
>
> I have created by myself the entity and the metaModel 
>
> @StaticMetamodel( Usuario.class ) 
> public abstract class Usuario_ { 
>
>         public static volatile SingularAttribute<Usuario, Integer> id; 
>         public static volatile SingularAttribute<Usuario, String> nome; 
>         public static volatile SingularAttribute<Usuario, String> password; 
>         public static volatile SingularAttribute<Usuario, String> login; 
>         public static volatile SingularAttribute<Usuario, Status> status; 
>         
> } 
>
> but its not working, so, what could be wrong ?? is there any docs for
> configure eclipse to auto generate these metamodels ?? ty
Comment 1 Michael OBrien CLA 2009-12-16 02:43:23 EST
>Since DI 42 it was decided that we can always assume that the javaClass will be set on the RelationalDescriptor
Currently there is no NPE handling for a null javaType set in the ManagedType contructor.

    protected ManagedTypeImpl(MetamodelImpl metamodel, RelationalDescriptor descriptor) {
        // A valid descriptor will always have a javaClass set
        super(descriptor.getJavaClass());

>In this manually generated case - it looks like we will need corner case handling for a null descriptor.javaClass
Comment 2 Michael OBrien CLA 2009-12-16 08:40:17 EST
Diego,
    Hi, I think I may have figured out your issue.
    Your persistence unit is not initilized in your DAO - since you are using the EMF directly, you are running an SE PU which requires that you create the EM before attempting to use the PU - there is no predeploy before you attempt to get the CriteriaBuilder (which uses the Metamodel)
    Normally, you would either get the EMF via injection or bootstrap Persistence class and get the EM via CreateEntityManager(SE) or via injection(EE) - thereby doing a predepoy (where the metamodel is pre-initialized properly)

    Your Fix:
-----------------
       add the following call to your code before emf.getCriteriaBuilder() - you may use entityManager.getCriteriaBuilder
       EntityManager entityManager = emf.createEntityManager()

    Reproduction:
-----------------
    If I insert the following invalid code before EM creation - I get the same NPE as you.
    by inserting emf.getCriteriaBuilder() before we do an emf.createEntityManager()
    private void initialize(String puName) {
        try {
                // Initialize an application managed JPA emf and em via META-INF
                emf  = Persistence.createEntityManagerFactory(puName);
invalid--> emf.getCriteriaBuilder(); // EM is not created or initialized yet - this inserted line is invalid code as the descriptors will not be initialized
                entityManager = emf.createEntityManager();           


    Note that it was decided in DI 42 that we would not check for a null javaClass on the ClassDescriptor - this would be an invalid state - therefore we assume that all RelationalDescriptors always have their javaClass set

Currently there is no NPE handling for a null javaType set in the ManagedType contructor - as this is an invalid state.
    protected ManagedTypeImpl(
        MetamodelImpl metamodel, RelationalDescriptor descriptor) {
        // A valid descriptor will always have a javaClass set
        super(descriptor.getJavaClass());

[EL Finest]: 2009-12-16 08:22:47.492--ServerSession(8880493)--Thread(Thread[main,5,main])--End predeploying Persistence Unit dao.create.tables.derby; session default-session; state Predeployed; factoryCount 1
java.lang.NullPointerException
    at org.eclipse.persistence.internal.jpa.metamodel.MetamodelImpl.initialize(MetamodelImpl.java:370)
    at org.eclipse.persistence.internal.jpa.metamodel.MetamodelImpl.<init>(MetamodelImpl.java:101)
    at org.eclipse.persistence.internal.jpa.metamodel.MetamodelImpl.<init>(MetamodelImpl.java:120)
    at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.getMetamodel(EntityManagerSetupImpl.java:1939)
    at org.eclipse.persistence.internal.jpa.EntityManagerFactoryImpl.getMetamodel(EntityManagerFactoryImpl.java:472)
    at org.eclipse.persistence.internal.jpa.EntityManagerFactoryImpl.getCriteriaBuilder(EntityManagerFactoryImpl.java:456)
    at org.eclipse.persistence.example.jpa.server.common.DDLGenerationClient.initialize(DDLGenerationClient.java:149)

    If you use a valid entityManager to get the criteriaBuilder then you will have no issues.
    thank you
    /michael
Comment 3 Michael OBrien CLA 2009-12-16 09:30:31 EST
>DI 99 has run into this null javaClass issue around an invalid metadata state but previously we ignored the invalid state

http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#DI_99:_20091110:_Metamodel.types_LinkedHashMap_appears_to_have_null.3Dnull_K.2CV_pairs_-_expected_Java_SE_behavior

If you put a breakpoint at line MetamodelImpl.java:235 - I had a secondary check for an invalid metadata state but we ignore it by design

    private boolean putType(Class javaClassKey, TypeImpl typeValue) {
        boolean isValid = true;
        // DI99: Check for an invalid key without reporting it (a non-Fail-Fast pattern)
        if(null == javaClassKey) {
--->        isValid = false;
        }
        this.types.put(javaClassKey, typeValue);
        return isValid;
    }

>We should not be putting null key:value pairs in the Map without warning about the metadata descriptor state

this	MetamodelImpl  (id=71)	
javaClassKey	null	
typeValue	EmbeddableTypeImpl<X>  (id=129)	
	descriptor	RelationalDescriptor  (id=126)	
	javaClass	null	
	members	null	
	metamodel	MetamodelImpl  (id=71)	
isValid	false
Comment 4 Michael OBrien CLA 2010-08-10 10:29:48 EDT
>See possibly related bug# 303063 (using the Metamodel via EntityManagerFactory before creating an EntityManager - where all descriptors are fully initialized)
>Attempting to reproduce the user state for bug# 303063 where the javaClass is null during Metamodel initialization resulted in the following
We have the following sequence of operations
- 1) predeploy for each persistence unit
			EntityManagerSetupImpl.predeploy(PersistenceUnitInfo, Map) line: 837	
     1a) in each predeploy we process metadata (used later by the Metamodel)
			PersistenceUnitProcessor.processORMetadata(MetadataProcessor, boolean) line: 432	

- 2) predeploy complete - we attempt to get the metamodel from the EMF (this is INVALID)
EntityManagerSetupImpl.getMetamodel() line: 2003	
- 3) we fail on an uninitialized descriptor during metamodel processing
RelationalDescriptor(ClassDescriptor).getJavaClass() line: 2055	
EmbeddableTypeImpl<X>(ManagedTypeImpl<X>).<init>(MetamodelImpl, RelationalDescriptor) line: 128	


>Processing Embeddables and MappedSuperclasses at this point is invalid because of the getMetamodel() call between the predeploy() and deploy() - descriptors (the entities) are not initialized yet
>How would one get to this point? do a getMetamodel() on the EntityManagerFactory before the EntityManager is deployed in EE or SE

>In the embeddable desriptor below the initializationStage == 0 == UNINITIALIZED
We need stage INITIALIZED == 2 (we dont need POST_INITIALIZED == 3)
this	RelationalDescriptor  (id=290)	
	initializationStage	0	
	javaClass	null	
	javaClassName	"org.eclipse.persistence.testing.models.jpa.metamodel.EmbeddedPK" (id=308)	
	wrapperPolicy	null	

>Fix: add a check for initializationStatus() during a ClassDeescriptor.getJavaClass() call from Metamodel API
Warn that Metamodel initialization is not possible before deployment has finished as the descriptor is not fully initialized
Comment 5 Michael OBrien CLA 2010-08-17 22:19:32 EDT
Created attachment 176855 [details]
Do a getServerSession that will do a login/deploy as part of an entityManagerFactory.predeploy() so all metadata processing, ddlgeneration and metamodel initialization is complete before a create em

>See design issue 106 option 3 for bug # 322585
http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#DI_106:_20100810:_322585:Metamodel_initialization_via_EMF.getMetamodel.28.29_before_EM_deploy_results_in_an_invalid_Metamodel_because_of_UNINITIALIZED_descriptor.initializationStage

After trying 2 locations of a getServerSession() call in order to do a login before an official deploy() on createEntityManager - I came across code that already exists for JPA validation that will do a login for us and effect a deploy just after the predeploy finishes on the EMF. 

>shouldGetSessionOnCreateFactory()

>Performance Results 106-3
The following show that that the time taken to update the session, do DDL generation and intialize the metamodel and canonical metamodel has shifted to the EMF creation from the EM creation. In essence an emf.createEntityManager() now takes just a couple microseconds instead of seconds. 
Doing all the following in the predeploy 
  updateServerSession 
  session.login 
  generateDDL 
  metamodel initalization 
  canonical metamodel initialization 
takes in SE 
> with change 
  predeploy() = 2.6 sec 
   [EL Finest]: 2010-08-17 18:01:12.002--ServerSession(27196165)--Thread(Thread[main,5,main])--Begin predeploying Persistence Unit dao.create.tables.derby; session default-session; state Initial; factoryCount 0 
   [EL Finest]: 2010-08-17 18:01:14.67--ServerSession(27196165)--Thread(Thread[main,5,main])--End deploying Persistence Unit dao.create.tables.derby; session default-session; state Deployed; factoryCount 1 
  deploy() = 0.09 sec 
   [EL Finest]: 2010-08-17 18:01:14.67--ServerSession(27196165)--Thread(Thread[main,5,main])--End deploying Persistence Unit dao.create.tables.derby; session default-session; state Deployed; factoryCount 1 
   [EL Finer]: 2010-08-17 18:01:14.763--ServerSession(27196165)--Thread(Thread[main,5,main])--client acquired 
 
> without change 
  predeploy() = 0.75 sec 
   [EL Finest]: 2010-08-17 17:58:29.169--ServerSession(2279771)--Thread(Thread[main,5,main])--Begin predeploying Persistence Unit dao.create.tables.derby; session default-session; state Initial; factoryCount 0 
   [EL Finest]: 2010-08-17 17:58:29.934--ServerSession(2279771)--Thread(Thread[main,5,main])--End predeploying Persistence Unit dao.create.tables.derby; session default-session; state Predeployed; factoryCount 1 
  deploy() = 1.87 sec 
   [EL Finest]: 2010-08-17 17:58:29.934--ServerSession(2279771)--Thread(Thread[main,5,main])--Begin deploying Persistence Unit dao.create.tables.derby; session default-session; state Predeployed; factoryCount 1 
   [EL Finer]: 2010-08-17 17:58:31.806--ServerSession(2279771)--Thread(Thread[main,5,main])--client acquired 

On emf.createEntityManager() we just skip deploy/login in getServerSession(), login in createEntityManagerImpl as well as deploy/login in getServerSession() in the EntityManagerImpl constructor 
because the emf.createEntityManager() does virtually nothing after this change 

EE Server testing on WebLogic 10.3.3.0 via standard @PersitenceContext injection on a SSB @EJB injected on a servlet client is OK 

Notice that a predeploy also does a deploy now - previously the deploy was only done when the client persisted or read from the persistence unit (via injection) 

Daemon Thread [[ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.Default (self-tuning)'] (Suspended (breakpoint at line 314 in EntityManagerSetupImpl))	
	EntityManagerSetupImpl.deploy(ClassLoader, Map) line: 314	
	EntityManagerFactoryImpl.getServerSession() line: 157	
	PersistenceProvider.createContainerEntityManagerFactory(PersistenceUnitInfo, Map) line: 243	
	PersistenceUnitInfoImpl.createEntityManagerFactory(boolean) line: 352	
	PersistenceUnitInfoImpl.createEntityManagerFactory() line: 332	
	PersistenceUnitInfoImpl.<init>(PersistenceUnitBean, PersistenceUnitConfigurationBean, GenericClassLoader, String, URL, URL) line: 134	
	ModulePersistenceUnitRegistry(AbstractPersistenceUnitRegistry).storeDescriptors(Map, Map) line: 336	
	ModulePersistenceUnitRegistry(AbstractPersistenceUnitRegistry).loadPersistenceDescriptor(VirtualJarFile, boolean, File) line: 250	
	ModulePersistenceUnitRegistry.<init>(GenericClassLoader, ApplicationContextInternal, Module, boolean) line: 69	
	EJBModule.setupPersistenceUnitRegistry() line: 221	
	EJBModule$1.execute() line: 322	
	PersistenceUnitRegistryInitializer.setupPersistenceUnitRegistries() line: 62	
	WebAppModule.prepare() line: 398	
	ScopedModuleDriver.prepare() line: 176	
	ModuleListenerInvoker.prepare() line: 199	
	DeploymentCallbackFlow$1.next(Object) line: 507	
	StateMachineDriver.nextState(StateChange, Object[]) line: 41	
	DeploymentCallbackFlow.prepare(Module[]) line: 149	
	DeploymentCallbackFlow.prepare() line: 45	
	BaseDeployment$1.next(Object) line: 1221	
	StateMachineDriver.nextState(StateChange, Object[]) line: 41	
	EarDeployment(BaseDeployment).prepare(DeploymentContext) line: 367	
	EarDeployment.prepare(DeploymentContext) line: 58	
	DeploymentStateChecker.prepare(DeploymentContext) line: 154	
	AppContainerInvoker.prepare(DeploymentContext) line: 60	
	RedeployOperation.createAndPrepareContainer() line: 98	
	RedeployOperation.doPrepare() line: 122	
	RedeployOperation(AbstractOperation).prepare() line: 217	
	DeploymentManager.handleDeploymentPrepare(Deployment, DeploymentManager$DeploymentRequestInfo) line: 747	
	DeploymentManager.prepareDeploymentList(ArrayList, DeploymentContext) line: 1216	
	DeploymentManager.handlePrepare(DeploymentContext) line: 250	
	DeploymentServiceDispatcher.prepare(DeploymentContext) line: 159	
	DeploymentReceiverCallbackDeliverer.doPrepareCallback(DeploymentContext) line: 171	
	DeploymentReceiverCallbackDeliverer.access$000(DeploymentReceiverCallbackDeliverer, DeploymentContext) line: 13	
	DeploymentReceiverCallbackDeliverer$1.run() line: 46	
	SelfTuningWorkManagerImpl$WorkAdapterImpl.run() line: 528	
	ExecuteThread.execute(Runnable) line: 201	
	ExecuteThread.run()
Comment 6 Michael OBrien CLA 2010-09-03 10:46:27 EDT
Created attachment 178157 [details]
Option 7 (in use): Do a getServerSession that does a deploy/login that calls initializeDescriptors only for early emf.getMetamodel() users - em.getMetamodel() users do not login
Comment 7 Michael OBrien CLA 2010-09-03 15:21:14 EDT
>See the fix for bug # 322585 in SVN rev# 8141 for 2.2 and 8142 for 2.1.2
https://fisheye2.atlassian.com/changelog/eclipselink/?cs=8141
We now do a preemptive DB login via getServerSession to force initializeDescriptors so that a MappedSuperclass javaClass is set on the descriptor when a client does an emf.getMetamodel() or emf.getCriteriaBuilder() before at least one EntityManager is created or logged in.
If a NullPointerException occurs on early post-predeploy but pre-deploy access to the Metamodel via an EntityManagerFactory.getMetamodel() or emf.getCriteriaBuilder() call before an initial login to the DB - then reopen this bug.
NPE in ManagedTypeImpl.getTypeClassFromAttributeOrMethodLevelAccessor
NPE in MetamodelImpl.initialize
Comment 8 Eclipse Webmaster CLA 2022-06-09 10:05:27 EDT
The Eclipselink project has moved to Github: https://github.com/eclipse-ee4j/eclipselink
Comment 9 Eclipse Webmaster CLA 2022-06-09 10:10:43 EDT
The Eclipselink project has moved to Github: https://github.com/eclipse-ee4j/eclipselink