Understanding jboss

zhaozj2021-02-16  52

Understanding jboss

Forest Hou

Write this article in English, I want to see if I can express it in another text. There is nothing else.

This article analyzes some critical techniques of JBoss:. Interceptor, dynamic proxy in rmi, and automatic object persistence It is based on JBoss 2.2.1 source code Through this article you can not only understand how JBoss implements the EJB model, but also realize. these techniques could be used in other projects. The UML pictures only show the critical classes that would help you to understand the code. The code is abstracted from JBoss software to describe these concepts.

Basic EJB Interfaces

1.1 Does EJB SPECification Only Define Interfaces? We can understand Major Parts of EJB by Above Class Diagram. It Shows The Relationship of Interfaces.

1.2 Using HelloWorld as an example, the EJB developer needs implement at least following classes: public interface Hello extends EJBObject; public interface HelloHome extends EJBHome; public class HelloBean implements SessionBean (or EntityBean);

1.3 "Hello" and "HelloHome" are remote interfaces. It is the EJB container (or application server) 's responsibility to implement the remote object and stub objects. The remote object delegates remote call for the enterprise bean (here is HelloBean). Hence The Container Separates The Enterprise Bean from Remote World, And Adds Diverse Services for the Enterprise Bean.

1.4 The Container Should Also Implements EJBCONText and Ejbmetata Interfaces for a Specified Enterprise Bean.

2. JBoss Infrastructure

................ ..

2.2 An application contains several enterprise beans and it creates a Container instance for each enterprise bean. You can regard the container as the implementation of the EJBObject and EJBHome interfaces.2.3 The configuration information of Enterprise beans is described in the ejb-jar.xml file . Their containers are configured in the standardjboss.xml and jboss.xml files. The ejb-jar.xml and jboss.xml files are included in the application jar file as the metadata. The standardjboss.xml defines the stand bean containers (CMP EntityBean , BMP EntityBean, Stateless SessionBean, Stateful SessionBean, Message Driven bean). Because the ejb-jar.xml file defines the bean type, JBoss can select the container type for an enterprise bean, then create the container instance for it.

. 2.4 The "deployments" field of the ContainerFactory class stores current deployed applications ContainerFactory is a singleton object; it is used to create different containers for an application The application server can run many applications simultaneously..

.

2.6 The container is responsible to automatically persist its enterprise bean. This is the most attractive feature of EJB. The StatefulSessionPersistenceManager and EntityPersistenceManager interfaces are called by the container when it decides to persist an enterprise bean instance. Because the Stateless bean has no state, it NEED NOT to Be Persisted.

2.7 WHEN A Remote Call Enters a Container Instance, IT Must First Pass Through The Interceptor List. When All Interceptors Agree The Request The IT ITED To The Enterprise Bean Instance.

2.8 The InstancePool is used to store unused enterprise beans. When the container find the requested enterprise bean is not in the memory (actually cache), it get an empty instance from the InstancePool and initialize the bean. Of course it returns the unused instance to the InstancePool.2.9 As its name indicates, the InstanceCache is used to cache the enterprise beans. The container first find the enterprise bean from the cache, if it failed, then it try to load the bean instance by the PersistenceManager. The InstanceCache is never Used for the center.

2.10 The container always tries to use the invoking thread. That means it uses the ContainerInvoker thread to finish the task. Supposing two remote calls enter the container, and calling the same method of the same enterprise bean. These two requests are in different threads, and the container will create two enterprise beans to serve them, but actually these beans are identical. So the thread fire extends to the background of the enterprise beans. You should understand why the EJB specification prohibits using multithread now. :-)

3. Interceptors

3.1 Interceptor is the critical part of JBoss Above figure shows different interceptor relationships All concrete interceptors are inherited from the AbstractInterceptor JBoss implements following interceptors...:

LogInterceptor implements logging service. MetricsInterceptor gathers data from the container for administration. SecurityInterceptor implements EJB declarative security model. TxInterceptorCMT is container managed transaction interceptor. TxInterceptorBMT is bean managed transaction interceptor. StatelessSessionInstanceInterceptor acquires the specified stateless session bean instance. StatefulSessionInstanceInterceptor acquires the specified stateful session bean instance. EntityInstanceInterceptor acquires the specified entity bean instance. EntitySynchronizationInterceptor takes care of EntityBean persistence.3.2 The enterprise bean related messages are stored in the MethodInvocation argument of the invoke () and invokeHome () methods. Even the bean instance itself is embedded into the EnterpriseContext Instance, Which is Also Stored Into The MethodInvocation Instance.

3.3 The Concrete Interceptors Should Overload The Invoke () And InvokeHome () Methods of Abstractinterceptor Class, And Must Pass The Call To The Next Interceptor as Following:

Public Object Invoke (MethodInvocation MI) THROWS Exception {// DO this Interceptor's actual work here. Return getNext (). Invoke (mi);}

3.4 Interceptors Areadded to the Container Through Following Scenario:

// org.jboss.ejb.ContainerFactory // standardjboss.xml defines interceptors for each kind of container. InitializeContainer () {// container is org.jboss.ejb.Container // conf.getContainerInterceptorsConf () is org.w3c.dom .Element ContainerInterceptors.addinterceptors (Container, ..., conf.getcontainerinterceptorsconf ());

// org.jboss.ejb.ContainerInterceptors addInterceptors (Container container, ..., Element element) {// get the iterator for "interceptor" Iterator interceptorElements = Metadata.getChildrenByTagName (element, "interceptor"); ClassLoader loader = container. GetClassLoader (); // First Build The Container Interceptor Stack from InterceptoreLeference Arraylist IStack = New ArrayList ();

while (! interceptorElements = null && interceptorElements.hasNext ()) {Element ielement = (Element) interceptorElements.next (); String className = MetaData.getElementContent (ielement); Class clazz = loader.loadClass (className); Interceptor interceptor = ( Interceptor) CLAZZ.NEWINSTANCE (); istack.add (interceptor);

// now add the interceptors to the contact force; i

// org.jboss.ejb.container // CreateContainerInterceptor IS An Abstract Method Abstract Interceptor CreateContainerInterceptor ();

// // for statelesssessioncontainer // // org.jboss.ejb.StateLessSessionContainer Interceptor CreateContainerInterceptor () {return new containerinterceptor ();

// this class is an inner class of statelesssessioncontainer // this is the last step beginning infoCation - All Interceptors area Done Class ContainerInterceptor Implements Interceptor {...}

// // for StatefulSessionContainer // // org.jboss.ejb.StatelessSessionContainer Interceptor createContainerInterceptor () {return new ContainerInterceptor ();} // This class is an inner class of StatefulSessionContainer // This is the last step before invocation - all Interceptors Are Done Class ContainerInterceptor IMPLEments Interceptor {...}

Something Interesting Things in Above Code:

The interceptors are added to the container in the order declared in the XML file. The last interceptor is always an instance of an interceptor defined in the concrete container, such as StatefulSessionContainer.ContainerInterceptor. We will see later that it is this special interceptor that actually Calls The Enterprise Bean Methods.

3.5 Let's Look At Standjboss.xml File, And List Session Bean's Interceptors:

Standard Stateless SessionBean

Loginterceptor

SecurityInterceptor txinterceptorcmt metricsinterceptor statelesssessioninstanceinterceptor statelesssessioninstanceinterceptor txinterceptorbmt Metrics Interceptor

Standard Stateful SessionBean

Loginterceptor

Txinterceptorcmt Metrics Interceptor statefulsessioninstanceinterceptor StatefulSessionInstanceInterceptor TxinterceptorBmt Metricsinterceptor SecurityInterceptor

3.6 The Calling Sequence RemoteObject-> ContainerInvoker-> Interceptor-> Enterprise Bean is intending. In this article we Only Analyze Following Scenario:

RemoteObject-> ContainerInvoker StatefulsessionInstanceInterceptor Last Interceptor-> Enterprise Bean Method

RemoteObject-> ContainerInvoker uses Dynamic Proxy technique. StatefulSessionInstanceInterceptor shows how the application server uses instance pool and instance cache, persists object (the most attractive part of application server). The "Last interceptor-> Enterprise Bean method" tells us how our bean methods Are Called. You will Understand How Jboss Works if You Master these Scenarios.4. from client to containerinvokeer

4.1 From above discussion we know every enterprise bean has a container, and every container has a ContainerInvoker. This container invoker (org.jboss.ejb.plugins.jrmp.server.JRMPContainerInvoker, we call it plugin invoker) inherits the java.rmi. Server.RemoteServer, SO IT IS A Remote Object, And ITS Remote Interface IS ContainerRemote. in Plugin Invoker's Start () Method:

// Org.jboss.ejb.plugins.jrmp.server.containerinvoker start () {UnicastRemoteObject.ExportObject (this, rmiport, clientsocketfactory, serversocketfactory);}

So this Remote Object Is Exported by The UnicastRemoteObject, And The Remote Call First Comes Into this Object. It is the container's Gate to the Enterprise Bean.

4.2 Although Jboss Creates The Stub IS Not Bound To The JNDI Server. The Actual Object Bound To The JNDI Server Is A Proxy Object:

// org.jboss.ejb.plugins.jrmp.server.ContainerInvoker start () {// after exprot the JRMPContainerInvoker (plugin invoker) object rebind (context, container.getBeanMetaData (). GetJndiName (), ((ContainerInvokerContainer) container) .getcontainerinvoker (). getjbhome ()); // Above red code Equals: this.getejbhome ()}

. So complicated invocation of getEJBHome () It is the plugin invoker itself, and its getEJBHome () method is: // org.jboss.ejb.plugins.jrmp.server.ContainerInvoker public EJBHome getEJBHome () {return ciDelegate.getEJBHome () }

Because dynamic proxy is implemented in jdk1.3, and JBoss supports jdk1.2, so it uses delegation to implement proxy object. The ciDelegate can be instance of org.jboss.plugins.jrmp13.server.JRMPContainerInvoker (v13 invoker), or org .jboss.plugins.jrmp12.server.jrmpcontainerinvokeker (V12 Invoker). We Only Look AT V13 Invoker:

// Org.jboss.ejb.plugins.jrmp13.server.containerinvoker public ejbhome getEjbhome () {return home;}

// org.jboss.ejb.plugins.jrmp13.server.ContainerInvoker // v13 invoker creates the proxy instance public void init () {this.home = (EJBHome) Proxy.newProxyInstance (((ContainerInvokerContainer) container) .getHomeClass () .getClassLoader (), new class [] {(ContainerContainer) Container) .gethomeClass (), Handleclass} (), Ci.Getjndiname (), Ci.Getejbmetata (), ci, ci.isoptimized ());}

OH, Another Long Invocation. We Must Realize That "CI" is the plugin invoker (org.jboss.ejb.containerinvoker). So The Remote Object is passed to the homeproxy as a parameter.

// org.jboss.ejb.plugins.jrmp.interfaces.HomeProxy // This class extends org.jboss.ejb.plugins.jrmp.interfaces.GenericProxy public HomeProxy (String name, EJBMetaData ejbMetaData, ContainerRemote container, boolean optimize) {super (Name, Container, Optimize);

// org.jboss.ejb.plugins.jrmp.interfaces.GenericProxy protected GenericProxy (String name, ContainerRemote container, boolean optimize) {// the plugin invoker is at last be stored in the GenericProxy this.container = container;} // org.jboss.ejb.plugins.jrmp.interfaces.GenericProxy // GenericProxy implements java.io.Externalizable interface, so it is serializable. public void writeExternal (java.io.ObjectOutput out) throws IOException {... // show container Serialization Out.writeObject (islocal ()? Container: null); ...}

// org.jboss.ejb.plugins.jrmp.interfaces.GenericProxy public void readExternal (java.io.ObjectInput in) throws IOException, ClassNotFoundException {... // show container serialization container = (ContainerRemote) in.readObject (); ...}

Notice when the GenericProxy is serialized to the JNDI server, the writeExternal () method is called, and the container (JRMPContainerInvoker) will be changed to JRMPContainerInvoker_Stub So when the client find the GenericProxy instance, it is different from that in the server side.:

Client Side: GenericProxy JRMPCONTAINERINVOKER_STUB Server Side: GenericProxy JRMPCONTAINVOKER

So the dynamic proxy holds the stub object and delegates the remote call for client! Compared to Rickard's first version talked in RMI mailing list, this one is much simpler. His first version creates the stub by himself, and uses complicated classloader to create the stub Object, But this Version Uses RMI's Stub Object Directly. :-d)

4.3 OK, Let's Look Atho HomeProxy REALIZE The Invoke () Method Required by The InvokerHandler Interface:

// org.jboss.ejb.plugins.jrmp.interfaces.HomeProxy public Object invoke (Object proxy, Method m, Object [] args) throws Throwable {// Build a method invocation that carries the identity of the target object RemoteMethodInvocation rmi = New RemoteMethodInvocation (New Cachekey (Args [0]), RemoveObject, New Object [0]); // Here Is The Critical Part Return Container.Invoke (New MarshalledObject (RMI)). Get ();}

So the HomeProxy invokes the stub (container) method at last, and that invocation goes to server Before that invocation the proxy can add many other features:. Authentication message, transaction, etc. Here is the proxy's strength.

4.4 At Server Side, The Plugin Invoker Method IS:

// org.jboss.ejb.plugins.jrmp.server.JRMPContainerInvoker public MarshalledObject invoke (MarshalledObject mimo) {RemoteMethodInvocation rmi = (RemoteMethodInvocation) mimo.get (); ... return new MarshalledObject (container.invoke (new MethodInvocation (rmi (), RMI.GetArguments (), RMI.GetPrincipal (), RMI.Getcredential (), RMI.GetCredential (), RMI.GetTransactionProPagationContext ())))))))))

// Org.jboss.ejb.StatefulSessionContainer public object invoke (MethodInvocation MI) {// Enter Interceptor stack! return getInterceptor (). Invoke (mi);}

We see the invocation enters the interceptor stack at last. There, the application server makes log record, checks access permissions, propagates transaction context, and of course, it must cache the enterprise bean instance according to the object id hidden in the MethodInvocation instance, And Calls the specified method on this specified instance. so let's see how jboss loads the Enterprise bean instance.5. loading Enterprise Bean Instance

5.1 The InstanceInterceptor (EntityInstanceInterceptor, StatefulSessionInstanceInterceptor, StatelessSessionInstanceInterceptor) response loading the specified enterprise bean instance into the context, and pass the call to the next interceptor.

5.2 for example, the invoke () Method of StatefulSessionInstanceInterceptor IS:

Object Invoke (MethodInvocation MI) {EnterpriseContext CTX = Container.getInstanceCache (). Get (mi.getid ()); mi.seTenterpriseContext (CTX);

Try {getNext (). Invoke (mi);} ...}

The enterprise bean instance is hidden in the EnterpriseContext instance, and this object is loaded by the InstanceCache object, and then is put in the MethodInvocation instance. Notice the MethodInvocation instance is passed as the parameter of the interceptors.

5.3 Following is The Skeleton of The EnterpriseContext Class:

Public Abstract Class EnterpriseContext {Object Instance; // EJB Instance Container Con; // The Container Using this Context Object ID; // Only StatelessSession Beans Have No ID, Stateful and Entity DO

// ejbcontext area imported in the ... protected class ejbcontextImpl imports ejbcontext {}}

Note The EJBCONText Is Implement AS An Inner Class of EnterpriseContext.

5.4 The get () method of instancecache is usimented in the AbstractInstanceCache class: // Org.jboss.ejb.plugins.abstractInstanceCache

Public EnterpriseContext Get (Object ID) {EnterpriseContext CTX = Cache_Policy.Get (ID); if (CTX == Null) {CTX = acquirecontext (); setKey (ID, CTX); Activate (CTX); INSERT (key, ctx) Return CTX;}

Protected Abstract EnterpriseContext AcquiRecontext () Throws Exception; Protected Abstract Void Activate (EnterpriseContext CTX) THROWS RemoteException;

Public void insert (EnterpriseContext CTX) {cachepolicy cache = getCache (); object key = getKey (CTX);

IF (cache.peek (key) == null) {cache.insert (key, ctx);}}

The InstanceCache first tries to get the context from the cache policy. If failed, it gets the context by the acquireContext () method and then call activate (), at last store it in the cache. Note the acquireContext () and activate () Are Abstract Methods.

5.5 Here Is The Code of StatefulSessionInstanceCache Which Implements The Above Abstract Methods:

// Org.jboss.ejb.plugins.StatefulsessionInstanceCache protected ctx {m_container.getPersistenceManager (). ActivateSession (CTX);

Protected Abstract EnterpriseContext AcquiRecontext () {Return M_Container.getInstancePool (). get ();}

...............................

5.6 Notice from the CachePolicy to InstancePool, then to PersistenceManager, all method invocation happened in the interfaces. At last the context is loaded and cached! The interface boundary is very clear.6. Automatic Object Persistence

6.1 The logic of automatic object persistence is hidden in the mist. It is finished by the WorkerQueue class. This class maintains an Executable object queue, and persists the Executable object in the queue by another thread.

// Org.jboss.util.WorkerQueue

/ * The thread this runs the executable jobs * / protected thread m_queuethread;

Public workerqueue (string threadname) {m_queuethread = new thread (new queueloop (), threadname);}

// The queueloop is an inner class of workerqueue protected class queueloop imports Runnable {public void run () {while {getJob (). Execute ()

6.2 For the LRUCachePolicy, when you insert a bean context into it, it may remove a recently less used context and try to create an Executable object with this context, and then insert that Executable object into the WorkerQueue.

// org.jboss.util.lrucachepolicy

// the map holding the cached objects protected;

// The Linked List Used to Implement The Lru Algorithm Protected Lrulist M_List;

Public void insert (Object Key, Object O) {Object Obj = m_map.get (key); // Try to locate the object if (obj == null) {m_list.demote (); lrucacheentry entry = CreateCachentry (key, o ); M_list.promote (entry); m_map.put (key, entry);}}

Protected void age (lrucacheentry entry) {transry.m_key);}

// inner class of LRUCachePolicy public class LRUList {protected void demote () {... if (m_count == m_maxCapacity) {LRUCacheEntry entry = m_tail; // the entry will be removed by ageOut ageOut (entry);}}}

The Lru Algorithm is Not Important Here. We Only NEED TO KNOW That The Demote () Method May Remove An entry by Ageout Method. But The ageout () of LRUCACHEPOLICY ONLY Removes The Object.

6.3 But the lruenterpriseContextCachepolicy real does Something in the ageout () Method:

// // org.jboss.ejb.plugins.lruenterPriseContextCachepolicy // Private AbstractInstanceCache M_Cache

Protected Void Ageout (LRUCACHEENTRY) {... m_cache.release (EnterpriseContext);}

// // org.jboss.ejb.plugins.abstractInstanceCache // public void release (EnterpriseContext CTX) {Object ID = getKey (CTX); if (getCache (). peek (id)! = null) {getCache (). REMOVE (ID);

SchedulePassivation (CTX);

/ * Helper Class That Handles Synchronization for the passivation thread * / private passivationHelper m_passivationhelper;

Protected Void SchedulePassivation (EnterpriseContext CTX) {m_passivationHelper.schedule (CTX);

6.4 The Actual Job Is Done in The PassivationHelper Class. It generate The Executable Object and PUSHES IT INTO The WorkerQueue.

// AbstractInstanceCache private workerQueue m_passivator;

// // inner class of AbstractInstanceCache // protected class PassivationHelper {protected void schedule (EnterpriseContext bean) {PassivationJob job = new PassivationJob (bean); m_passivator.putJob (job);}}

6.5 The PassivationJob IMPLEments The Executable Interface:

// The PassivationJob is defined in the PassivationHelper class // as an anonymous class new PassivationJob (bean) {public void execute () {EnterpriseContext ctx = getEnterpriseContext (); // complicated algorithm, but runs following methods: passivate (ctx); FreeContext (CTX);}}

6.6 Well the passivate () And freecontext () Are Defined As Abstract Methods in The AbstractInstanceCache Class. The StatefulSessionInstanceCache Class Implements Them.

// Org.jboss.ejb.plugins.StatefulsessionInstanceCache protected passivate (EnterpriseContext CTX) {m_container.getPersistenceManager (). PassiVateSession (CTX);

Protected Void FreeContext (EnterpriseContext CTX) {m_container.getinstancepool (). free (ctx);

SO at last the bean object is personistent and the context is returned to the instancepool. The logic is perfect.

6.7 The Timecachepolicy Implements The Runnable Interface. That Means It Checks The Cached Objects Periodly and age out the timed-out objects.

7. Call Bean Method

THE Last Step is Simple, The ContainerCeptor Locates The Method by The Method Name, and Gets The Bean Instance from The Context, and the Kicks Off The Method on The Bean!

// StatefulSessionContainer.ContainerInterceptor

Invoke (MethodInvocation MI) {

Method M = (Method) BeanMapping.get (mi.getMethod ());

Return M.Invoke (mi.getenterpriseContext (). getInstance (),

mi.getarguments ());

}

8. Conclusion

转载请注明原文地址:https://www.9cbs.com/read-25177.html

New Post(0)