-
1. Multiple instances of @Singleton
jaikiran Apr 27, 2011 2:37 AM (in response to cpuffalt)Please post the relevant code and the logs which shows this behaviour. Also the client code which accesses the singleton.
-
2. Re: Multiple instances of @Singleton
cpuffalt Apr 27, 2011 3:12 AM (in response to jaikiran)The @Singleton class:
{code}
@Singleton @ConcurrencyManagement(BEAN)
public class ElasticSearchBean
{
private static final Logger log = LoggerFactory.getLogger(ElasticSearchBean.class);
private static final String cluster = "dev-calgary";
private static final AtomicInteger instanceCounter = new AtomicInteger(0);
private final int instance = instanceCounter.incrementAndGet();
private final Node node;
private final Client client;
public ElasticSearchBean()
{
log.info("******************************************");
log.info("Connecting to our elastic search cluster! " + instance);
log.info("******************************************");
try
{
node = nodeBuilder().data(false).clusterName(cluster).node();
client = node.client();
}
catch(RuntimeException e)
{
log.error("Exception initializing client!", e);
throw e;
}
}
@SuppressWarnings("unused") @PostConstruct
private void init()
{
log.info("******************************************");
log.info("Connected! " + instance);
log.info("******************************************");
}
@SuppressWarnings("unused") @PreDestroy
private void shutdown()
{
log.info("******************************************");
log.info("Shutting down our search node and client! " + instance);
log.info("******************************************");
client.close();
node.close();
}
public Client client()
{
log.info("Returning client instance from " + instance);
return client;
}
}
{code}
The MDB that references it:
{code}
@MessageDriven(
activationConfig =
{
@ActivationConfigProperty(
propertyName = "destination", propertyValue = "/queue/indexQueue"),
@ActivationConfigProperty(
propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
@ActivationConfigProperty(
propertyName = "subscriptionDurability", propertyValue = "Durable"),
@ActivationConfigProperty(
propertyName = "clientId", propertyValue = "indexer"),
@ActivationConfigProperty(
propertyName = "subscriptionName", propertyValue = "indexerSub")
})
public class IndexerMsgBean implements MessageListener
{
private static final Logger log = LoggerFactory.getLogger(IndexerMsgBean.class);
@Inject
private ElasticSearchBean search;
@Override
public void onMessage(Message msg)
{
log.info("******************************************");
log.info("Got a message! " + msg);
log.info("******************************************");
if (msg instanceof TextMessage)
{
TextMessage txtmsg = (TextMessage)msg;
Client client = search.client();
try
{
client.prepareIndex("messages", "msg", "1")
.setSource(jsonBuilder()
.startObject()
.field("msg").value(txtmsg.getText())
.endObject())
.execute()
.actionGet();
}
catch (ElasticSearchException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
catch (JMSException e)
{
e.printStackTrace();
}
}
}
}
{code}
Relevant log snippet:
00:25:19,001 INFO [org.jboss.ejb3.nointerface.impl.jndi.AbstractNoInterfaceViewBinder] Binding the following entry in Global JNDI for bean:StartupBean
StartupBean/no-interface -> EJB3.1 no-interface view
00:25:19,015 INFO [org.geekden.jee6.bean.ElasticSearchBean] ******************************************
00:25:19,016 INFO [org.geekden.jee6.bean.ElasticSearchBean] Connecting to our elastic search cluster! 1
00:25:19,017 INFO [org.geekden.jee6.bean.ElasticSearchBean] ******************************************
00:25:19,575 INFO [org.geekden.jee6.bean.StartupBean] ******************************************
00:25:19,575 INFO [org.geekden.jee6.bean.StartupBean] Sending a message now!
00:25:19,577 INFO [org.geekden.jee6.bean.StartupBean] ******************************************
00:25:19,813 INFO [org.geekden.jee6.bean.ElasticSearchBean] ******************************************
00:25:19,813 INFO [org.geekden.jee6.bean.ElasticSearchBean] Connecting to our elastic search cluster! 3
00:25:19,814 INFO [org.geekden.jee6.bean.ElasticSearchBean] ******************************************
00:25:19,808 INFO [org.geekden.jee6.bean.ElasticSearchBean] ******************************************
00:25:19,818 INFO [org.geekden.jee6.bean.ElasticSearchBean] Connecting to our elastic search cluster! 2
00:25:19,818 INFO [org.geekden.jee6.bean.ElasticSearchBean] ******************************************
Sorry about the formatting. For some reason my {code} tags don't seem to be working.
-
3. Re: Multiple instances of @Singleton
wolfc Apr 27, 2011 4:46 AM (in response to cpuffalt)You are seeing multiple instances of the class, not the EJB.
A no-interface view is an extension of the bean class, so each time a no-interface view is looked up you'll get a new instance of the class. The EJB lifecycle itself is handled through the appropriate lifecycle callbacks (post-construct & pre-destroy). You must not rely on the constructor.
-
4. Re: Multiple instances of @Singleton
cpuffalt Apr 27, 2011 10:46 AM (in response to wolfc)Carlo,
Thanks for the explanation. So by adding an interface I might be able to work around this and keep my variables marked as final, or even then it would not be recommended?
Thanks again,
Corey
-
5. Multiple instances of @Singleton
genman Apr 27, 2011 5:14 PM (in response to cpuffalt)Don't use a constructor and don't assign member variables in the class.
If you want, delegate the construction to another class.
-
6. Re: Multiple instances of @Singleton
cpuffalt Apr 28, 2011 1:04 AM (in response to genman)Ok, here's what my code looks like now:
{code}
@Singleton @ConcurrencyManagement(BEAN)
public class ElasticSearchBean
{
private static final Logger log = LoggerFactory.getLogger(ElasticSearchBean.class);
private static final String cluster = "dev-calgary";
private volatile Node node;
private volatile Client client;
@SuppressWarnings("unused") @PostConstruct
private void init()
{
log.info("******************************************");
log.info("Connecting to our elastic search cluster!");
log.info("******************************************");
try
{
node = nodeBuilder().data(false).clusterName(cluster).node();
client = node.client();
}
catch(RuntimeException e)
{
log.error("Exception initializing client!", e);
throw e;
}
log.info("******************************************");
log.info("Connected!");
log.info("******************************************");
}
@SuppressWarnings("unused") @PreDestroy
private void shutdown()
{
log.info("******************************************");
log.info("Shutting down our search node and client!");
log.info("******************************************");
client.close();
node.close();
}
public Client client()
{
log.info("******************************************");
log.info("Returning client instance.");
log.info("******************************************");
return client;
}
}
{code}
Anyone see any issues with that? It works fine so far in my testing. Alternatively I could use container-managed concurrency with a @Lock(READ) on my client() method but this way there's no contention at all. Any holes in my theory?
Thanks again for your help. It still bothers me that I can't use "final" to indicate that my instance variables are immutable but I guess there's no way around that today (maybe EJB8?)
Regards,
Corey
-
7. Re: Multiple instances of @Singleton
jaikiran Apr 28, 2011 1:39 AM (in response to wolfc)Either I am not reading the question correctly or I'm missing something. Why are there multiple instances of that @Singleton bean class being created as a result of lookup? And if that's what is happening, I don't see how a state can be maintained in a singleton bean (which is expected to maintain states across calls for the lifetime of the application). This looks like a bug to me.
Corey, by the way I see you use @Inject in the MDB to get hold of the singleton bean. Can you replace it to use @EJB instead:
public class IndexerMsgBean implements MessageListener { private static final Logger log = LoggerFactory.getLogger(IndexerMsgBean.class); @javax.ejb.EJB private ElasticSearchBean search;
I haven't looked at how @Inject is implemented so I don't know if that is a problem. See if using @EJB prevents the multiple instance creation.
-
8. Re: Multiple instances of @Singleton
cpuffalt Apr 28, 2011 2:34 AM (in response to jaikiran)Jaikiran,
I switched to using the @EJB annotation as you suggested but I still see the bean being constructed multiple (though possibly, fewer) times. I haven't read the spec myself to see if this should be considered a bug or not. Clearly Carlo & Elias feel this is normal and that all initialization has to happen in the @PostConstruct method rather than the constructor. If that's the case then I think it's unfortunate that the EJB spec is forcing developers to use coding patterns different from idiomatic Java (although EJB 3.1 is a vast improvement over the older incarnations of course).
Regards,
Corey
-
9. Re: Multiple instances of @Singleton
wolfc Apr 29, 2011 11:05 AM (in response to jaikiran)Ah my favorite pasttime: quoting spec.
EJB 3.1 FR 3.4.4 Session Bean's No-Interface View:
The developer of an enterprise bean that exposes a no-interface view must not make any assumptions
about the number of times the bean class no-arg constructor will be called. For example, it is possible
that the acquisition of a client reference to the no-interface view will result in the invocation of the-
bean-class constructor. It is recommended that the bean developer place component initialization logic
in a @PostConstruct method instead of the bean class no-arg constructor.
-
10. Re: Multiple instances of @Singleton
jaikiran Apr 29, 2011 11:31 AM (in response to wolfc)Ah yes! I now get it and understand what I missed. I clearly did not fully interpret what you said earlier:
A no-interface view is an extension of the bean class, so each time a no-interface view is looked up you'll get a new instance of the class.
Makes sense now
-
11. Re: Multiple instances of @Singleton
bersling Jan 31, 2018 2:06 PM (in response to jaikiran)Thank you! In my case that was the problem all along...
Can you replace it to use @EJB instead:
Wrong import... So for all the others:
- javax.ejb.Singleton goes together with @javax.ejb.EJB.
- javax.inject.Singleton goest together with javax.inject.Inject