Search code examples
javajava-ee-6ejb-3.1

@Startup @Singleton instantiated twice in WebLogic (EJB 3.1)


I have a class marked @Startup and @Singleton and the constructor is being called twice.

Why is it being called twice?

  • WebLogic 12.1.1
  • Running Locally (not cluster)
  • @PostConstruct is also called twice when it is there
  • Nothing relevant in XML configuration (weblogic-ejb-jar.xml et al)

Here is the class:

import java.util.concurrent.atomic.AtomicInteger;

import javax.ejb.Singleton;
import javax.ejb.Startup;

@Startup
@Singleton
public class CacheStartupListener {

    static AtomicInteger count= new AtomicInteger(0); 

    public CacheStartupListener() {
        System.err.println("Singleton invoked " + count.incrementAndGet() + " " + getClass().getClassLoader().toString());  
    }
}

I can see from the output the the constructor is being called from the same classloader both times.

The stacktrace from within the constructor both lead through wlfullclient-12.1.1jar, but the stacktraces are otherwise different.

Here is the stack trace from the first instantiation:

Daemon Thread [[ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.Default (self-tuning)'] (Suspended (breakpoint at line 30 in CacheStartupListener)) (out of synch)   
    CacheStartupListener_m3hhum_NoIntfViewImpl(CacheStartupListener).<init>() line: 30 (out of synch)   
    CacheStartupListener_m3hhum_NoIntfViewImpl.<init>(SingletonLocalObject) line: not available 
    NativeConstructorAccessorImpl.newInstance0(Constructor, Object[]) line: not available [native method]   
    NativeConstructorAccessorImpl.newInstance(Object[]) line: 57    
    DelegatingConstructorAccessorImpl.newInstance(Object[]) line: 45    
    Constructor<T>.newInstance(Object...) line: 525 
    SingletonEJBLocalHomeImpl.allocateBI(Class<T>) line: 45 
    SingletonEJBLocalHomeImpl.prepare() line: 30    
    SingletonSessionBeanInfoImpl(SessionBeanInfoImpl).prepare() line: 458   
    SingletonSessionBeanInfoImpl.prepare() line: 115    
    EJBDeployer.setupBeanInfos() line: 767  
    EJBDeployer.prepare(VirtualJarFile, EjbDescriptorBean) line: 920    
    EJBModule.prepare() line: 419   
    ScopedModuleDriver.prepare() line: 188  
    ExtensibleModuleWrapper.prepare() line: 83  
    ModuleListenerInvoker.prepare() line: 100   
    ModuleStateDriver$1.next(Module) line: 172  
    ModuleStateDriver$1.next(Object) line: 167  
    StateMachineDriver<StateMachine>.nextState(StateChange<StateMachine>, StateMachine[]) line: 35  
    ModuleStateDriver.prepare(Module[]) line: 38    
    DeploymentCallbackFlow.prepare(Module[]) line: 139  
    DeploymentCallbackFlow.prepare() line: 55   
    BaseDeployment$1.next(Object) line: 706 
    StateMachineDriver<StateMachine>.nextState(StateChange<StateMachine>, StateMachine[]) line: 35  
    EarDeployment(BaseDeployment).prepare(DeploymentContext) line: 237  
    EarDeployment.prepare(DeploymentContext) line: 61   
    DeploymentStateChecker.prepare(DeploymentContext) line: 158 
    AppContainerInvoker.prepare(DeploymentContext) line: 60 
    RedeployOperation.createAndPrepareContainer() line: 104 
    RedeployOperation.doPrepare() line: 138 
    RedeployOperation(AbstractOperation).prepare() line: 229    
    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: 545   
    ExecuteThread.execute(Runnable) line: 256   
    ExecuteThread.run() line: 221   

And here is the stack trace for the second instantiation:

Daemon Thread [[ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.Default (self-tuning)'] (Suspended (breakpoint at line 30 in CacheStartupListener)) (out of synch)   
    CacheStartupListener_m3hhum_Impl(CacheStartupListener).<init>() line: 30 (out of synch) 
    CacheStartupListener_m3hhum_Impl.<init>() line: not available   
    NativeConstructorAccessorImpl.newInstance0(Constructor, Object[]) line: not available [native method]   
    NativeConstructorAccessorImpl.newInstance(Object[]) line: 57    
    DelegatingConstructorAccessorImpl.newInstance(Object[]) line: 45    
    Constructor<T>.newInstance(Object...) line: 525 
    WeldConstructorImpl<T>.newInstance(Object...) line: 204 
    ConstructorInjectionPoint<T>.newInstance(BeanManagerImpl, CreationalContext<?>) line: 117   
    ExtendedSessionBean(SessionBean<T>).createInstance(CreationalContext<T>) line: 212  
    ExtendedSessionBean.derivedCreateInstance(CreationalContext) line: 35   
    WeldEjbBeanManager$ExtendedInjectionTarget.produce(CreationalContext) line: 131 
    WeldEjbBeanManager.newBeanInstance(String) line: 78 
    InjectionBasedEjbComponentCreator.getBean(String, Class, boolean) line: 75  
    SingletonSessionManager(BaseEJBManager).createNewBeanInstance() line: 209   
    SingletonSessionManager.constructAndInitBean() line: 353    
    SingletonSessionManager.access$300(SingletonSessionManager) line: 63    
    SingletonSessionManager$SingletonLifecycleManager.doActualInit() line: 798  
    SingletonSessionManager$SingletonLifecycleManager.initInternal(boolean) line: 744   
    SingletonSessionManager$SingletonLifecycleManager.init() line: 631  
    SingletonSessionManager.init() line: 280    
    SingletonSessionManager.perhapsInit() line: 276 
    EJBDeployer.initializeBeans() line: 1287    
    EJBDeployer.start() line: 1174  
    EJBModule.start() line: 590 
    ModuleStateDriver$3.next(Module) line: 213  
    ModuleStateDriver$3.next(Object) line: 208  
    StateMachineDriver<StateMachine>.nextState(StateChange<StateMachine>, StateMachine[]) line: 35  
    ModuleStateDriver.start(Module[]) line: 70  
    ScopedModuleDriver.start() line: 212    
    ExtensibleModuleWrapper.start() line: 111   
    ModuleListenerInvoker.start() line: 124 
    ModuleStateDriver$3.next(Module) line: 213  
    ModuleStateDriver$3.next(Object) line: 208  
    StateMachineDriver<StateMachine>.nextState(StateChange<StateMachine>, StateMachine[]) line: 35  
    ModuleStateDriver.start(Module[]) line: 70  
    StartModulesFlow.activate() line: 24    
    BaseDeployment$2.next(Object) line: 729 
    StateMachineDriver<StateMachine>.nextState(StateChange<StateMachine>, StateMachine[]) line: 35  
    EarDeployment(BaseDeployment).activate(DeploymentContext) line: 258 
    EarDeployment.activate(DeploymentContext) line: 61  
    DeploymentStateChecker.activate(DeploymentContext) line: 165    
    AppContainerInvoker.activate(DeploymentContext) line: 79    
    RedeployOperation(AbstractOperation).activate(Deployment) line: 582 
    RedeployOperation(ActivateOperation).activateDeployment() line: 148 
    RedeployOperation(ActivateOperation).doCommit() line: 114   
    RedeployOperation(AbstractOperation).commit() line: 335 
    DeploymentManager.handleDeploymentCommit(Deployment, AbstractOperation) line: 844   
    DeploymentManager.activateDeploymentList(ArrayList, DeploymentManager$DeploymentRequestInfo) line: 1253 
    DeploymentManager.handleCommit(DeploymentContext) line: 440 
    DeploymentServiceDispatcher.commit(DeploymentContext) line: 163 
    DeploymentReceiverCallbackDeliverer.doCommitCallback(DeploymentContext) line: 195   
    DeploymentReceiverCallbackDeliverer.access$100(DeploymentReceiverCallbackDeliverer, DeploymentContext) line: 13 
    DeploymentReceiverCallbackDeliverer$2.run() line: 68    
    SelfTuningWorkManagerImpl$WorkAdapterImpl.run() line: 545   
    ExecuteThread.execute(Runnable) line: 256   
    ExecuteThread.run() line: 221   

Solution

  • The first stack is not actually creating a bean instance but a proxy instance. Your @Singleton does not declare any business interfaces, so by default it uses the no-interface view (which can be explicitly configured using @LocalBean). When using the no-interface view, the container generates a proxy class that is a subclass of your class (in this case, the proxy is named CacheStartupListener_m3hhum_NoIntfViewImpl). When the container creates an instance of this proxy subclass, the constructor of your EJB class will be invoked. You can try printing getClass() in your constructor to prove this point.

    I strongly recommend removing the Java constructor altogether and moving all initialization logic to @PostConstruct. Java constructors do not mix well with no-interface view.