4 Replies Latest reply on May 25, 2007 10:23 AM by brian.stansberry

    JBoss EJB3 SLSB Failover

    milmber

      Hi,
      I'm running two vanilla instances of on the same machines using the "all" configuration. I have a webapp that looks up the SLSB. This works when the TestEJB.jar and TestWebApp.war is on the same node. When I delete the TestEJB.jar from the node where TestWebApp.war is deployed...I expect TestWebApp.war to lookup the TestSessionBean(in TestEJB.jar on both nodes) to be looked up via HAJNDI.

      This doesn't work though...plus the TestSessionBean is not listed in the HAJNDI section using JNDIView in the jmx-console? Am I missing something here - as I figure this should work out of the box using JBoss 4.2.0.GA?

      My code is as follows:

      TestSessionBean implementation

      package com.xxx.test.ejb3.sessionbean;
      
      import org.jboss.annotation.ejb.Clustered;
      import org.jboss.annotation.ejb.LocalBinding;
      import org.jboss.annotation.ejb.RemoteBinding;
      import org.jboss.ha.framework.interfaces.RoundRobin;
      import org.apache.log4j.Logger;
      
      import javax.ejb.Stateless;
      import javax.ejb.Local;
      import javax.ejb.Remote;
      import javax.persistence.EntityManager;
      import javax.persistence.PersistenceContext;
      import javax.persistence.Query;
      
      import com.xxx.test.ejb3.entityejb.TestEntity;
      
      import java.util.List;
      
      @Remote(TestSession.class)
      @Local(TestSession.class)
      @Stateless(name = "TestSessionEJB")
      @Clustered(loadBalancePolicy = RoundRobin.class)
      public class TestSessionBean implements TestSession {
       private static final Logger log = Logger.getLogger(TestSession.class);
       @PersistenceContext
       protected EntityManager entityManager;
      
      
       public TestSessionBean() {
       }
      
       public void testMethod() {
       log.info("testMethod() called....");
       }
      
       public void persistEntity() {
       log.info("persisteEntity() called....");
       TestEntity testEntity = new TestEntity();
       testEntity.setName("tom");
       testEntity.setSurname("jones");
       entityManager.persist(testEntity);
       }
      
       @SuppressWarnings("unchecked")
       public TestEntity[] getPersistedEntities() {
       Query query = entityManager.createQuery("select t from TestEntity t");
       List<TestEntity> testEntities = query.getResultList();
       return testEntities.toArray(new TestEntity[testEntities.size()]);
       }
      }
      


      TestSession interface:

      package com.xxx.test.ejb3.sessionbean;
      
      import com.xxx.test.ejb3.entityejb.TestEntity;
      
      import javax.ejb.Remote;
      import javax.ejb.Local;
      import java.util.List;
      
      public interface TestSession {
      
       public void testMethod();
      
       public void persistEntity();
      
       public TestEntity[] getPersistedEntities();
      }
      


      jboss.xml:
      <?xml version="1.0"?>
      <!DOCTYPE jboss PUBLIC "-//JBoss//DTD JBOSS 4.0//EN"
       "http://www.jboss.org/j2ee/dtd/jboss_4_0.dtd">
      <jboss>
      </jboss>
      


      Global JNDI Namespace:
       +- TestSessionEJB (class: org.jnp.interfaces.NamingContext)
       | +- local (proxy: $Proxy95 implements interface com.xxx.test.ejb3.sessionbean.TestSession,interface org.jboss.ejb3.JBossProxy,interface javax.ejb.EJBLocalObject)
       | +- remote (proxy: $Proxy94 implements interface com.xxx.test.ejb3.sessionbean.TestSession,interface org.jboss.ejb3.JBossProxy)
      


      The HA-JNDI Namespace is empty...

      From the logs my cluster of 2 nodes seems to be up and running correctly...
      23:58:19,580 INFO [DefaultPartition] New cluster view for partition DefaultPartition (id: 1, delta: 1) : [192.168.1.12:1099, 192.168.1.12:1199]
      23:58:19,582 INFO [DefaultPartition] I am (192.168.1.12:1099) received membershipChanged event:
      23:58:19,582 INFO [DefaultPartition] Dead members: 0 ([])
      23:58:19,582 INFO [DefaultPartition] New Members : 1 ([192.168.1.12:1199])
      23:58:19,582 INFO [DefaultPartition] All Members : 2 ([192.168.1.12:1099, 192.168.1.12:1199])
      


      My client JSP to look up the Session Bean looks as follows:
      <%@ page import="com.xxx.test.ejb3.entityejb.TestEntity" %>
      <%@ page import="com.xxx.test.ejb3.sessionbean.TestSession" %>
      <%@ page import="javax.naming.InitialContext" %>
      <%@ page import="java.util.Properties" %>
      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <%
       TestSession testSessionBean = null;
       try {
       Properties props = new Properties();
       props.put(InitialContext.PROVIDER_URL, "192.168.1.12:1100");
       props.put(InitialContext.URL_PKG_PREFIXES, "jboss.naming:org.jnp.interfaces");
       props.put(InitialContext.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
       InitialContext initialContext = new InitialContext(props);
       testSessionBean = (TestSession) initialContext.lookup("TestSessionEJB/remote");
       } catch (Exception e) {
       e.printStackTrace();
       }
       if (request.getParameter("operation") != null && request.getParameter("operation").equalsIgnoreCase("method")) {
       testSessionBean.testMethod();
       } else
       if (request.getParameter("operation") != null && request.getParameter("operation").equalsIgnoreCase("persist")) {
       testSessionBean.persistEntity();
       }
       TestEntity[] testEntities = testSessionBean.getPersistedEntities();
      %>
      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <html>
      <head><title>Simple test jsp page</title></head>
      <body>
      <a href="index.jsp?operation=method">dummy method</a>
      <a href="index.jsp?operation=persist">persist</a><br>
      <% for (int i=0;i<testEntities.length;i++) {%>
       <%=testEntities.getId()%> <%=testEntities.getName()%> <%=testEntities.getSurname()%><br>
       <%}%>
       </body>
       </html>
      


      and finally, my stacktrace when I delete the TestEJB.jar on one server:

      00:05:45,399 ERROR [STDERR] javax.naming.CommunicationException [Root exception is java.lang.ClassNotFoundException: com.xxx.test.ejb3.sessionbean.TestSession (no security manager: RMI class loader disabled)]
      00:05:45,399 ERROR [STDERR] at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:728)
      00:05:45,399 ERROR [STDERR] at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:587)
      00:05:45,400 ERROR [STDERR] at javax.naming.InitialContext.lookup(InitialContext.java:351)
      00:05:45,400 ERROR [STDERR] at org.apache.jsp.index_jsp._jspService(index_jsp.java:71)
      00:05:45,400 ERROR [STDERR] at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
      00:05:45,400 ERROR [STDERR] at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
      00:05:45,400 ERROR [STDERR] at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:387)
      00:05:45,400 ERROR [STDERR] at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:320)
      00:05:45,400 ERROR [STDERR] at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:266)
      00:05:45,400 ERROR [STDERR] at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
      00:05:45,400 ERROR [STDERR] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
      00:05:45,400 ERROR [STDERR] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
      00:05:45,400 ERROR [STDERR] at org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96)
      00:05:45,400 ERROR [STDERR] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
      00:05:45,400 ERROR [STDERR] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
      00:05:45,401 ERROR [STDERR] at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:230)
      00:05:45,401 ERROR [STDERR] at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
      00:05:45,401 ERROR [STDERR] at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:179)
      00:05:45,401 ERROR [STDERR] at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:84)
      00:05:45,401 ERROR [STDERR] at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
      00:05:45,401 ERROR [STDERR] at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:104)
      00:05:45,401 ERROR [STDERR] at org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:156)
      00:05:45,401 ERROR [STDERR] at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
      00:05:45,401 ERROR [STDERR] at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:241)
      00:05:45,401 ERROR [STDERR] at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:844)
      00:05:45,401 ERROR [STDERR] at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:580)
      00:05:45,401 ERROR [STDERR] at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
      00:05:45,401 ERROR [STDERR] at java.lang.Thread.run(Thread.java:613)
      00:05:45,402 ERROR [STDERR] Caused by: java.lang.ClassNotFoundException: com.xxx.test.ejb3.sessionbean.TestSession (no security manager: RMI class loader disabled)
      00:05:45,402 ERROR [STDERR] at sun.rmi.server.LoaderHandler.loadProxyClass(LoaderHandler.java:531)
      00:05:45,402 ERROR [STDERR] at java.rmi.server.RMIClassLoader$2.loadProxyClass(RMIClassLoader.java:628)
      00:05:45,402 ERROR [STDERR] at org.jboss.system.JBossRMIClassLoader.loadProxyClass(JBossRMIClassLoader.java:82)
      00:05:45,402 ERROR [STDERR] at java.rmi.server.RMIClassLoader.loadProxyClass(RMIClassLoader.java:294)
      00:05:45,402 ERROR [STDERR] at sun.rmi.server.MarshalInputStream.resolveProxyClass(MarshalInputStream.java:238)
      00:05:45,402 ERROR [STDERR] at java.io.ObjectInputStream.readProxyDesc(ObjectInputStream.java:1500)
      00:05:45,402 ERROR [STDERR] at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1463)
      00:05:45,402 ERROR [STDERR] at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1699)
      00:05:45,402 ERROR [STDERR] at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1305)
      00:05:45,403 ERROR [STDERR] at java.io.ObjectInputStream.readObject(ObjectInputStream.java:348)
      00:05:45,403 ERROR [STDERR] at java.rmi.MarshalledObject.get(MarshalledObject.java:135)
      00:05:45,403 ERROR [STDERR] at org.jnp.interfaces.MarshalledValuePair.get(MarshalledValuePair.java:72)
      00:05:45,403 ERROR [STDERR] at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:652)
      00:05:45,403 ERROR [STDERR] ... 27 more
      00:05:45,404 ERROR [[jsp]] Servlet.service() for servlet jsp threw exception
      java.lang.NullPointerException
       at org.apache.jsp.index_jsp._jspService(index_jsp.java:76)
       at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
       at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
       at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:387)
       at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:320)
       at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:266)
       at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
       at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
       at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
       at org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96)
       at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
       at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
       at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:230)
       at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
       at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:179)
       at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:84)
       at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
       at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:104)
       at org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:156)
       at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
       at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:241)
       at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:844)
       at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:580)
       at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
       at java.lang.Thread.run(Thread.java:613)
      


        • 1. Re: JBoss EJB3 SLSB Failover
          brian.stansberry

          From a brief glance, it looks like after you undeploy your TestEJB.jar, the TestSession class is no longer on the classpath. Your web component can't deserialize the bean proxy after the lookup because the bean's interface class isn't available. You need to package things such that this class remains available.

          • 2. Re: JBoss EJB3 SLSB Failover
            milmber

            Hi,
            Thank for the reply. I've packaged my TestSession interface with the web application, but I get the following exception now:

            13:39:41,929 INFO [TomcatDeployer] deploy, ctxPath=/TestWebApp, warUrl=.../tmp/deploy/tmp46382TestWebApp-exp.war/
            13:41:04,824 ERROR [STDERR] java.lang.ClassCastException: $Proxy87
            13:41:04,825 ERROR [STDERR] at org.apache.jsp.index_jsp._jspService(index_jsp.java:71)
            13:41:04,825 ERROR [STDERR] at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
            13:41:04,825 ERROR [STDERR] at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
            13:41:04,825 ERROR [STDERR] at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:387)
            13:41:04,825 ERROR [STDERR] at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:320)
            13:41:04,825 ERROR [STDERR] at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:266)
            13:41:04,825 ERROR [STDERR] at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
            13:41:04,825 ERROR [STDERR] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
            13:41:04,825 ERROR [STDERR] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
            13:41:04,826 ERROR [STDERR] at org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96)
            13:41:04,826 ERROR [STDERR] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
            13:41:04,826 ERROR [STDERR] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
            13:41:04,826 ERROR [STDERR] at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:230)
            13:41:04,826 ERROR [STDERR] at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
            13:41:04,826 ERROR [STDERR] at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:179)
            13:41:04,826 ERROR [STDERR] at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:84)
            13:41:04,826 ERROR [STDERR] at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
            13:41:04,826 ERROR [STDERR] at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:104)
            13:41:04,827 ERROR [STDERR] at org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:156)
            13:41:04,827 ERROR [STDERR] at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
            13:41:04,827 ERROR [STDERR] at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:241)
            13:41:04,827 ERROR [STDERR] at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:844)
            13:41:04,827 ERROR [STDERR] at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:580)
            13:41:04,827 ERROR [STDERR] at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
            13:41:04,827 ERROR [STDERR] at java.lang.Thread.run(Thread.java:613)
            13:41:04,864 ERROR [[jsp]] Servlet.service() for servlet jsp threw exception
            java.lang.NullPointerException
             at org.apache.jsp.index_jsp._jspService(index_jsp.java:81)
             at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
             at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
             at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:387)
             at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:320)
             at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:266)
             at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
             at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
             at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
             at org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96)
             at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
             at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
             at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:230)
             at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
             at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:179)
             at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:84)
             at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
             at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:104)
             at org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:156)
             at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
             at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:241)
             at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:844)
             at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:580)
             at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
             at java.lang.Thread.run(Thread.java:613)
            


            My TestEJB.jar is packaged as follows:

            Archive: Documents/IdeaProjects/jboss_presentation/TestEJB/TestEJB.jar
             Length Date Time Name
             -------- ---- ---- ----
             52 05-25-07 13:38 META-INF/MANIFEST.MF
             0 05-25-07 13:38 com/
             0 05-25-07 13:38 com/xxx/
             0 05-25-07 13:38 com/xxx/test/
             0 05-25-07 13:38 com/xxx/test/ejb3/
             0 05-25-07 13:38 com/xxx/test/ejb3/entityejb/
             1172 05-25-07 13:38 com/xxx/test/ejb3/entityejb/TestEntity.class
             0 05-25-07 13:38 com/xxx/test/ejb3/sessionbean/
             261 05-25-07 13:38 com/xxx/test/ejb3/sessionbean/TestSession.class
             2317 05-25-07 13:38 com/xxx/test/ejb3/sessionbean/TestSessionBean.class
             0 05-25-07 13:38 com/xxx/test/jmx/
             1218 05-25-07 13:38 com/xxx/test/jmx/TestService.class
             250 05-25-07 13:38 com/xxx/test/jmx/TestServiceMBean.class
             485 05-24-07 11:54 META-INF/persistence.xml
             204 05-23-07 15:33 META-INF/jbosscmp-jdbc.xml
             147 05-24-07 23:09 META-INF/jboss.xml
             -------- -------
             6106 16 files
            



            And my TestWebApp.war looks as follows:

            Archive: Documents/IdeaProjects/jboss_presentation/TestWebApp/TestWebApp.war
             Length Date Time Name
             -------- ---- ---- ----
             52 05-25-07 13:38 META-INF/MANIFEST.MF
             0 05-25-07 13:38 WEB-INF/classes/
             0 05-25-07 13:38 WEB-INF/classes/com/
             0 05-25-07 13:38 WEB-INF/classes/com/xxx/
             0 05-25-07 13:38 WEB-INF/classes/com/xxx/test/
             0 05-25-07 13:38 WEB-INF/classes/com/xxx/test/ejb3/
             0 05-25-07 13:38 WEB-INF/classes/com/xxx/test/ejb3/sessionbean/
             261 05-25-07 13:38 WEB-INF/classes/com/xxx/test/ejb3/sessionbean/TestSession.class
             1840 05-25-07 00:02 index.jsp
             0 05-24-07 11:23 WEB-INF/
             190 05-24-07 11:23 WEB-INF/jboss-web.xml
             298 05-24-07 11:23 WEB-INF/web.xml
             -------- -------
             2641 12 files
            


            • 3. Re: JBoss EJB3 SLSB Failover
              milmber

              Tested this with an out of container client and it works as expected.

              • 4. Re: JBoss EJB3 SLSB Failover
                brian.stansberry

                You've now got two copies of the class in the system, loaded by separate classloaders. This causes type conflicts when the war tries to interact with locally deployed ejb.

                Simplest solution to this kind of thing is to package the war and the ejb jar in an EAR, with only one copy of the class, and deploy them as a unit.