blob: b785f2b8b44cfd630fe57158bbecd835c8b77605 [file] [log] [blame]
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<HTML>
<HEAD>
<!-- $PAGETITLE -->
<TITLE>OpenEJB - Singleton Example</TITLE>
<LINK href="http://openejb.apache.org/all.css" rel="stylesheet" type="text/css">
<!--[if IE]><link rel="stylesheet" type="text/css" media="screen, projection" href="openejb.apache.org/ie.css"><![endif]-->
<LINK rel="SHORTCUT ICON" href="http://openejb.apache.org/images/favicon.ico">
<META http-equiv="Content-Type" content="text/html;charset=UTF-8">
<SCRIPT language="javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.1/jquery.min.js" type="text/javascript"></SCRIPT>
<SCRIPT language="javascript" src="http://openejb.apache.org/tweet/jquery.tweet.js" type="text/javascript"></SCRIPT>
<SCRIPT type="text/javascript">
$(document).ready(function(){
$(".tweet").tweet({
avatar_size: 32,
count: 4,
fetch:25,
username: "openejb",
list: "contributors",
template:"{avatar}{text}",
filter: function(t){ return /openejb/i.test(t["tweet_raw_text"]); },
loading_text: "loading list..."
});
});
</SCRIPT>
</HEAD>
<BODY>
<!-- Delay the loading of the external javascript file needed for labels (as it takes too long to load and visibly holds loading of the page body) -->
<!-- To do this without javascript errors over undefined functions, we need to declare stubs here (that are overrided later by the proper implementations) -->
<SCRIPT language="JavaScript" type="text/javascript">
function doAddLabel(hideTextfieldAfterAddParam)
{
// stub
}
function onAddLabel()
{
// stub
}
function showLabelsInput()
{
// stub
}
</SCRIPT>
<A name="top"></A>
<TABLE class="frameTable" cellpadding="0" cellspacing="0" border="0">
<TR class="Row1">
<TD class="Col1"><IMG alt="" src="http://openejb.apache.org/images/dotTrans.gif"></TD>
<TD class="Col2"><IMG alt="" src="http://openejb.apache.org/images/dotTrans.gif"></TD>
<TD class="Col3"><IMG alt="" src="http://openejb.apache.org/images/dotTrans.gif"></TD>
<TD class="Col4"><IMG alt="" src="http://openejb.apache.org/images/dotTrans.gif"></TD>
<TD class="Col5"><IMG alt="" src="http://openejb.apache.org/images/dotTrans.gif"></TD>
</TR>
<TR class="Row2">
<TD class="Col1"><IMG alt="" src="http://openejb.apache.org/images/dotTrans.gif"></TD>
<TD class="Col2">&nbsp;</TD>
<TD class="Col3" id="breadcrumbs">
<!-- $TOP_NAV_BAR -->
<A href="index.html" title="Index">Home</A> | <A href="news.html" title="News">News</A> | <A href="faq.html" title="FAQ">FAQ</A> | <A href="download.html" title="Download">Download</A> | <A href="mailing-lists.html" title="Mailing Lists">Lists</A> | <A href="http://issues.apache.org/jira/browse/OPENEJB" class="external-link" rel="nofollow">Issues</A>
</TD>
<TD class="Col4"><IMG alt="" src="http://openejb.apache.org/images/dotTrans.gif"></TD>
<TD class="Col5">&nbsp;</TD>
</TR>
<TR class="Row3">
<TD class="Col1"><IMG alt="" class="Row3Img" id="thinLine" src="http://openejb.apache.org/images/line_sm.gif"></TD>
<TD class="Col2"><IMG alt="" class="Row3Img" src="http://openejb.apache.org/images/dotTrans.gif"></TD>
<TD class="Col3"><IMG alt="" class="Row3Img" src="http://openejb.apache.org/images/dotTrans.gif"></TD>
<TD class="Col4"><IMG alt="" src="http://openejb.apache.org/images/dotTrans.gif"></TD>
<TD class="Col5"><IMG alt="" class="Row3Img" src="http://openejb.apache.org/images/dotTrans.gif"></TD>
</TR>
<TR class="Row4">
<TD class="Col1">
<SPAN id="Navigation">
<H3><A name="Navigation-Overview"></A>Overview</H3>
<UL class="alternate" type="square">
<LI><A href="index.html" title="Index">Home</A></LI>
<LI><A href="news.html" title="News">News</A></LI>
<LI><A href="faq.html" title="FAQ">FAQ</A></LI>
<LI><A href="download.html" title="Download">Download</A></LI>
<LI><A href="../OPENEJBx30/index.html" title="Index">Documentation</A></LI>
<LI><A href="examples.html" title="Examples">Examples</A></LI>
<LI><A href="http://cwiki.apache.org/confluence/display/OPENEJB/Lightening%20Demos" class="external-link" rel="nofollow">Lightning Demos</A></LI>
<LI><A href="mailing-lists.html" title="Mailing Lists">Mailing Lists</A></LI>
<LI><A href="source-code.html" title="Source Code">Source Code</A></LI>
<LI><A href="http://blogs.apache.org/openejb" class="external-link" rel="nofollow">Project Blog</A></LI>
</UL>
<H3><A name="Navigation-Servers"></A>Servers</H3>
<UL class="alternate" type="square">
<LI><A href="local-server.html" title="Local Server">Local</A></LI>
<LI><A href="remote-server.html" title="Remote Server">Remote</A></LI>
</UL>
<H3><A name="Navigation-Integrations"></A>Integrations</H3>
<UL class="alternate" type="square">
<LI><A href="../OPENEJBx30/tomcat.html" title="Tomcat">Tomcat</A></LI>
<LI><A href="geronimo.html" title="Geronimo">Geronimo</A></LI>
<LI><A href="webobjects.html" title="WebObjects">WebObjects</A></LI>
</UL>
<H3><A name="Navigation-Community"></A>Community</H3>
<UL class="alternate" type="square">
<LI><A href="team.html" title="Team">Team</A></LI>
<LI><A href="articles.html" title="Articles">Articles</A></LI>
<LI><A href="http://webchat.freenode.net/?channels=openejb" class="external-link" rel="nofollow">IRC</A></LI>
</UL>
<H3><A name="Navigation-RelatedProjects"></A>Related Projects</H3>
<UL class="alternate" type="square">
<LI><A href="http://activemq.apache.org/" class="external-link" rel="nofollow">ActiveMQ</A></LI>
<LI><A href="http://openjpa.apache.org/" class="external-link" rel="nofollow">OpenJPA</A></LI>
<LI><A href="http://cxf.apache.org/" class="external-link" rel="nofollow">CXF</A></LI>
</UL>
<H3><A name="Navigation-Index"></A>Index</H3>
<UL class="alternate" type="square">
<LI><A href="space-index.html" title="Space Index">Site Index</A></LI>
<LI><A href="../OPENEJBx30/space-index.html" title="Space Index">Doc Index</A></LI>
</UL>
<H3>
<A name="Navigation-Feeds"></A>
Feeds
</H3>
<UL class="feeds">
<LI>
<A href="http://cwiki.apache.org/confluence/spaces/rss.action?key=OPENEJB&newPages=false">
<IMG src="http://openejb.apache.org/images/rss.gif"></A>
<A class="feedsText" href="http://cwiki.apache.org/confluence/spaces/rss.action?key=OPENEJB&newPages=false">Site</A>
</LI>
<LI><A href="http://cwiki.apache.org/confluence/spaces/blogrss.action?key=OPENEJB">
<IMG src="http://openejb.apache.org/images/rss.gif"></A>
<A class="feedsText" href="http://cwiki.apache.org/confluence/spaces/blogrss.action?key=OPENEJB">News</A>
</LI>
</UL>
</SPAN>
</TD>
<TD class="Col2">&nbsp;</TD>
<TD class="Col3">
<TABLE id="PageHeader" border="0" width="100%">
<TR>
<TD>
<A href="http://openejb.org/">
<IMG hspace="0" src="http://openejb.apache.org/images/logo_openejb.gif" vspace="0">
</A>
</TD>
<TD align="right">
<A href="http://www.apache.org/">
<IMG src="http://www.apache.org/images/asf-logo.gif" width="258" height="66">
</A>
</TD>
</TR>
<TR>
<TD id="page_title">
<!-- $TITLE -->
Singleton Example
</TD>
<TD align="right">
<BR><BR>
<!-- Google CSE Search Box Begins -->
<FORM id="searchbox_010475492895890475512:_t4iqjrgx90" action="http://www.google.com/cse">
<INPUT type="hidden" name="cx" value="010475492895890475512:_t4iqjrgx90">
<INPUT type="hidden" name="cof" value="FORID:0">
<INPUT name="q" type="text" size="25">
<INPUT type="submit" name="sa" value="Search">
</FORM>
<SCRIPT type="text/javascript" src="http://www.google.com/coop/cse/brand?form=searchbox_010475492895890475512:_t4iqjrgx90"></SCRIPT>
<!-- Google CSE Search Box Ends -->
</TD>
</TR>
</TABLE>
<P>
<!-- $BODY -->
<DIV id="PageContent">
<DIV class="panelMacro"><TABLE class="tipMacro"><COLGROUP><COL width="24"><COL></COLGROUP><TR><TD valign="top"><IMG src="https://cwiki.apache.org/confluence/images/icons/emoticons/check.gif" width="16" height="16" align="absmiddle" alt="" border="0"></TD><TD><B>OpenEJB 3.1 or later required</B><BR></TD></TR></TABLE></DIV>
<H1><A name="SingletonExample-Overview"></A>Overview</H1>
<P>As the name implies a javax.ejb.Singleton is a session bean with a guarantee that there is at most one instance in the application.</P>
<P>What it gives you that is completely missing in EJB 3.0 and prior versions is the ability to have an EJB that is notified when the application starts and notified when the application stops. So you can do all sorts of things that you previously could only do with a load-on-startup servlet. It also gives you a place to hold data that pertains to the entire application and all users using it, without the need for a static. Additionally, Singleton beans can be invoked by several threads at one time similar to a Servlet.</P>
<P><EM>See the <A href="../OPENEJBx30/singleton-beans.html" title="Singleton Beans">Singleton Beans</A> page for a full description of the javax.ejb.Singleton api.</EM></P>
<H1><A name="SingletonExample-TheCode"></A>The Code</H1>
<H2><A name="SingletonExample-PropertyRegistryBean"></A>PropertyRegistryBean</H2>
<P>Here we see a bean that uses the Bean-Managed Concurrency option as well as the <B>@Startup</B> annotation which causes the bean to be instantiated by the container when the application starts. Singleton beans with <B>@ConcurrencyManagement(BEAN)</B> are responsible for their own thread-safety. The bean shown is a simple properties &quot;registry&quot; and provides a place where options could be set and retrieved by all beans in the application.</P>
<DIV class="code panel" style="border-width: 1px;"><DIV class="codeContent panelContent">
<PRE class="code-java"><SPAN class="code-keyword">import</SPAN> <SPAN class="code-keyword">static</SPAN> javax.ejb.ConcurrencyManagementType.BEAN;
<SPAN class="code-keyword">import</SPAN> javax.ejb.Singleton;
<SPAN class="code-keyword">import</SPAN> javax.ejb.ConcurrencyManagement;
<SPAN class="code-keyword">import</SPAN> javax.ejb.Startup;
<SPAN class="code-keyword">import</SPAN> javax.annotation.PostConstruct;
<SPAN class="code-keyword">import</SPAN> javax.annotation.PreDestroy;
<SPAN class="code-keyword">import</SPAN> java.util.Properties;
@Singleton
@ConcurrencyManagement(BEAN)
@Startup
<SPAN class="code-keyword">public</SPAN> class PropertyRegistryBean <SPAN class="code-keyword">implements</SPAN> PropertyRegistry {
<SPAN class="code-comment">// Note the java.util.Properties object is a thread-safe
</SPAN> <SPAN class="code-comment">// collections that uses synchronization. If it didn't
</SPAN> <SPAN class="code-comment">// you would have to use some form of synchronization
</SPAN> <SPAN class="code-comment">// to ensure the PropertyRegistryBean is thread-safe.
</SPAN> <SPAN class="code-keyword">private</SPAN> <SPAN class="code-keyword">final</SPAN> Properties properties = <SPAN class="code-keyword">new</SPAN> Properties();
<SPAN class="code-comment">// The @Startup method ensures that <SPAN class="code-keyword">this</SPAN> method is
</SPAN> <SPAN class="code-comment">// called when the application starts up.
</SPAN> @PostConstruct
<SPAN class="code-keyword">public</SPAN> void applicationStartup() {
properties.putAll(<SPAN class="code-object">System</SPAN>.getProperties());
}
@PreDestroy
<SPAN class="code-keyword">public</SPAN> void applicationShutdown() {
properties.clear();
}
<SPAN class="code-keyword">public</SPAN> <SPAN class="code-object">String</SPAN> getProperty(<SPAN class="code-object">String</SPAN> key) {
<SPAN class="code-keyword">return</SPAN> properties.getProperty(key);
}
<SPAN class="code-keyword">public</SPAN> <SPAN class="code-object">String</SPAN> setProperty(<SPAN class="code-object">String</SPAN> key, <SPAN class="code-object">String</SPAN> value) {
<SPAN class="code-keyword">return</SPAN> (<SPAN class="code-object">String</SPAN>) properties.setProperty(key, value);
}
<SPAN class="code-keyword">public</SPAN> <SPAN class="code-object">String</SPAN> removeProperty(<SPAN class="code-object">String</SPAN> key) {
<SPAN class="code-keyword">return</SPAN> (<SPAN class="code-object">String</SPAN>) properties.remove(key);
}
}
</PRE>
</DIV></DIV>
<H2><A name="SingletonExample-ComponentRegistryBean"></A>ComponentRegistryBean</H2>
<P>Here we see a bean that uses the Container-Managed Concurrency option, the default. With <B>@ConcurrencyManagement(CONTAINER)</B> the container controls whether multi-threaded access should be allowed to the bean (<B>@Lock(READ)</B>) or if single-threaded access should be enforced (<B>@Lock(WRITE)</B>).</P>
<DIV class="code panel" style="border-width: 1px;"><DIV class="codeContent panelContent">
<PRE class="code-java"><SPAN class="code-keyword">import</SPAN> <SPAN class="code-keyword">static</SPAN> javax.ejb.LockType.READ;
<SPAN class="code-keyword">import</SPAN> <SPAN class="code-keyword">static</SPAN> javax.ejb.LockType.WRITE;
<SPAN class="code-keyword">import</SPAN> javax.ejb.Lock;
<SPAN class="code-keyword">import</SPAN> javax.ejb.Singleton;
<SPAN class="code-keyword">import</SPAN> java.util.HashMap;
<SPAN class="code-keyword">import</SPAN> java.util.Map;
<SPAN class="code-keyword">import</SPAN> java.util.Collection;
<SPAN class="code-keyword">import</SPAN> java.util.ArrayList;
@Singleton
@Lock(READ)
<SPAN class="code-keyword">public</SPAN> class ComponentRegistryBean <SPAN class="code-keyword">implements</SPAN> ComponentRegistry {
<SPAN class="code-keyword">private</SPAN> <SPAN class="code-keyword">final</SPAN> Map&lt;<SPAN class="code-object">Class</SPAN>, <SPAN class="code-object">Object</SPAN>&gt; components = <SPAN class="code-keyword">new</SPAN> HashMap&lt;<SPAN class="code-object">Class</SPAN>, <SPAN class="code-object">Object</SPAN>&gt;();
<SPAN class="code-keyword">public</SPAN> &lt;T&gt; T getComponent(<SPAN class="code-object">Class</SPAN>&lt;T&gt; type) {
<SPAN class="code-keyword">return</SPAN> (T) components.get(type);
}
<SPAN class="code-keyword">public</SPAN> Collection&lt;?&gt; getComponents() {
<SPAN class="code-keyword">return</SPAN> <SPAN class="code-keyword">new</SPAN> ArrayList(components.values());
}
@Lock(WRITE)
<SPAN class="code-keyword">public</SPAN> &lt;T&gt; T setComponent(<SPAN class="code-object">Class</SPAN>&lt;T&gt; type, T value) {
<SPAN class="code-keyword">return</SPAN> (T) components.put(type, value);
}
@Lock(WRITE)
<SPAN class="code-keyword">public</SPAN> &lt;T&gt; T removeComponent(<SPAN class="code-object">Class</SPAN>&lt;T&gt; type) {
<SPAN class="code-keyword">return</SPAN> (T) components.remove(type);
}
}
</PRE>
</DIV></DIV>
<P>Unless specified explicitly on the bean class or a method, the default @Lock value is @Lock(WRITE). The code above uses the @Lock(READ) annotation on bean class to change the default so that multi-threaded access is granted by default. We then only need to apply the @Lock(WRITE) annotation to the methods that modify the state of the bean.</P>
<P>Essentially @Lock(READ) allows multithreaded access to the Singleton bean instance <B>unless</B> someone is invoking an @Lock(WRITE) method. With @Lock(WRITE), the thread invoking the bean will be guaranteed to have exclusive access to the Singleton bean instance for the duration of its invocation. This combination allows the bean instance to use data types that are not normally thread safe. Great care must still be used, though. </P>
<P>In the example we see ComponentRegistryBean using a java.util.HashMap which is not synchronized. To make this ok we do three things:</P>
<OL>
<LI>Encapsulation. We don't expose the HashMap instance directly; including its iterators, key set, value set or entry set.</LI>
<LI>We use @Lock(WRITE) on the methods that mutate the map such as the put() and remove() methods.</LI>
<LI>We use @Lock(READ) on the get() and values() methods as they do not change the map state and are guaranteed not to be called at the same as any of the @Lock(WRITE) methods, so we know the state of the HashMap is no being mutated and therefore safe for reading.</LI>
</OL>
<P>The end result is that the threading model for this bean will switch from multi-threaded access to single-threaded access dynamically as needed depending on the which methods are being invoked. This gives Singletons a bit of an advantage over Servlets for processing multi-threaded requests.</P>
<P><EM>See the <A href="../OPENEJBx30/singleton-beans.html" title="Singleton Beans">Singleton Beans</A> page for more advanced details on Container-Managed Concurrency.</EM></P>
<H1><A name="SingletonExample-TestCase"></A>Test Case</H1>
<DIV class="code panel" style="border-width: 1px;"><DIV class="codeContent panelContent">
<PRE class="code-java"><SPAN class="code-keyword">public</SPAN> class ComponentRegistryBeanTest <SPAN class="code-keyword">extends</SPAN> TestCase {
<SPAN class="code-keyword">public</SPAN> void test() <SPAN class="code-keyword">throws</SPAN> Exception {
Properties props = <SPAN class="code-keyword">new</SPAN> Properties();
props.setProperty(Context.INITIAL_CONTEXT_FACTORY, <SPAN class="code-quote">&quot;org.apache.openejb.client.LocalInitialContextFactory&quot;</SPAN>);
InitialContext context = <SPAN class="code-keyword">new</SPAN> InitialContext(props);
<SPAN class="code-comment">// Both references below will point to the exact same instance
</SPAN> ComponentRegistry one = (ComponentRegistry) context.lookup(<SPAN class="code-quote">&quot;ComponentRegistryBeanLocal&quot;</SPAN>);
ComponentRegistry two = (ComponentRegistry) context.lookup(<SPAN class="code-quote">&quot;ComponentRegistryBeanLocal&quot;</SPAN>);
URI expectedUri = <SPAN class="code-keyword">new</SPAN> URI(<SPAN class="code-quote">&quot;foo:<SPAN class="code-comment">//bar/baz&quot;</SPAN>);
</SPAN>
one.setComponent(URI.class, expectedUri);
URI actualUri = two.getComponent(URI.class);
assertSame(expectedUri, actualUri);
Date expectedDate = <SPAN class="code-keyword">new</SPAN> Date();
two.setComponent(Date.class, expectedDate);
Date actualDate = one.getComponent(Date.class);
assertSame(expectedDate, actualDate);
}
}
</PRE>
</DIV></DIV>
<H1><A name="SingletonExample-Running"></A>Running</H1>
<P>Running the example is fairly simple. In the &quot;simple-singleton&quot; directory run:</P>
<P>$ mvn clean install</P>
<P>Which should create output like the following.</P>
<DIV class="preformatted panel" style="border-width: 1px;"><DIV class="preformattedContent panelContent">
<PRE>-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running org.superbiz.registry.ComponentRegistryBeanTest
Apache OpenEJB 3.1-SNAPSHOT build: 20080820-09:53
http://openejb.apache.org/
INFO - openejb.home = /Users/dblevins/work/openejb3/examples/simple-singleton
INFO - openejb.base = /Users/dblevins/work/openejb3/examples/simple-singleton
INFO - Configuring Service(id=Default Security Service, type=SecurityService, provider-id=Default Security Service)
INFO - Configuring Service(id=Default Transaction Manager, type=TransactionManager, provider-id=Default Transaction Manager)
INFO - Found EjbModule in classpath: /Users/dblevins/work/openejb3/examples/simple-singleton/target/classes
INFO - Beginning load: /Users/dblevins/work/openejb3/examples/simple-singleton/target/classes
INFO - Configuring enterprise application: classpath.ear
INFO - Configuring Service(id=Default Singleton Container, type=Container, provider-id=Default Singleton Container)
INFO - Auto-creating a container for bean ComponentRegistryBean: Container(type=SINGLETON, id=Default Singleton Container)
INFO - Enterprise application &quot;classpath.ear&quot; loaded.
INFO - Assembling app: classpath.ear
INFO - Jndi(name=ComponentRegistryBeanLocal) --&gt; Ejb(deployment-id=ComponentRegistryBean)
INFO - Jndi(name=PropertyRegistryBeanLocal) --&gt; Ejb(deployment-id=PropertyRegistryBean)
INFO - Created Ejb(deployment-id=ComponentRegistryBean, ejb-name=ComponentRegistryBean, container=Default Singleton Container)
INFO - Created Ejb(deployment-id=PropertyRegistryBean, ejb-name=PropertyRegistryBean, container=Default Singleton Container)
INFO - Deployed Application(path=classpath.ear)
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.879 sec
Running org.superbiz.registry.PropertiesRegistryBeanTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.009 sec
Results :
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
</PRE>
</DIV></DIV>
</DIV>
</P>
</TD>
<TD class="Col4"><IMG src="http://openejb.apache.org/images/dotTrans.gif"></TD>
<TD class="Col5">
</TD>
</TR>
<TR class="Row5">
<TD class="Col1">&nbsp;</TD>
<TD class="Col2">&nbsp;</TD>
<TD class="Col3">
<BR>
<BR>
<IMG width="100%" height="1" src="http://openejb.apache.org/images/line_light.gif">
<TABLE width="100%">
<TR>
<TD>
<SPAN class="bodyGrey">
<SMALL>
<NOTICE><!-- $FOOTER -->
Apache OpenEJB is an project of The Apache Software Foundation (ASF)
</NOTICE>
<BR>
Site Powered by
<A href="http://atlassian.com/">Atlassian</A>
<A href="http://atlassian.com/confluence/">Confluence</A>
.
</SMALL>
</SPAN>
</TD>
<TD align="right">
<A style="color:#999;font-size:small;font-weight:normal;" href="https://cwiki.apache.org/confluence/pages/editpage.action?spaceKey=OPENEJB&title=Singleton%20Example">[ edit ]</A>
</TD>
</TR>
</TABLE>
<BR>
</TD>
<TD class="Col4"><IMG src="http://openejb.apache.org/images/dotTrans.gif"></TD>
<TD class="Col5">&nbsp;</TD>
</TR>
</TABLE>
<!-- Needed for composition plugin -->
<!-- delay the loading of large javascript files to the end so that they don't interfere with the loading of page content -->
<SPAN style="display: none">
<SCRIPT type="text/javascript" language="JavaScript" src="http://cwiki.apache.org/confluence/labels-javascript"></SCRIPT>
<SCRIPT src="http://www.google-analytics.com/urchin.js" type="text/javascript">
</SCRIPT>
<SCRIPT type="text/javascript">
_uacct = "UA-2717626-1";
urchinTracker();
</SCRIPT>
</SPAN>
</BODY>
</HTML>