KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > proxy > ejb > ProxyFactory


1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */

22 package org.jboss.proxy.ejb;
23
24 import java.lang.reflect.Proxy JavaDoc;
25 import java.lang.reflect.Constructor JavaDoc;
26 import java.lang.reflect.InvocationHandler JavaDoc;
27 import java.util.ArrayList JavaDoc;
28 import java.util.Collection JavaDoc;
29 import java.util.HashMap JavaDoc;
30 import java.util.Iterator JavaDoc;
31 import java.rmi.ServerException JavaDoc;
32
33 import javax.ejb.EJBHome JavaDoc;
34 import javax.ejb.EJBObject JavaDoc;
35 import javax.ejb.EJBMetaData JavaDoc;
36 import javax.management.ObjectName JavaDoc;
37 import javax.naming.InitialContext JavaDoc;
38 import javax.naming.NamingException JavaDoc;
39
40 import org.jboss.deployment.DeploymentException;
41 import org.jboss.ejb.Container;
42 import org.jboss.ejb.EJBProxyFactory;
43 import org.jboss.ejb.EJBProxyFactoryContainer;
44 import org.jboss.invocation.Invocation;
45 import org.jboss.invocation.Invoker;
46 import org.jboss.invocation.InvocationContext;
47 import org.jboss.invocation.InvocationKey;
48 import org.jboss.logging.Logger;
49 import org.jboss.metadata.InvokerProxyBindingMetaData;
50 import org.jboss.metadata.MetaData;
51 import org.jboss.metadata.EntityMetaData;
52 import org.jboss.metadata.SessionMetaData;
53 import org.jboss.metadata.BeanMetaData;
54 import org.jboss.naming.Util;
55 import org.jboss.proxy.Interceptor;
56 import org.jboss.proxy.ClientContainer;
57 import org.jboss.proxy.ClientContainerEx;
58 import org.jboss.proxy.IClientContainer;
59 import org.jboss.proxy.ejb.handle.HomeHandleImpl;
60 import org.jboss.system.Registry;
61 import org.jboss.util.NestedRuntimeException;
62 import org.w3c.dom.Element JavaDoc;
63 import org.w3c.dom.Node JavaDoc;
64 import org.w3c.dom.NodeList JavaDoc;
65
66
67 /**
68  * As we remove the one one association between container STACK and invoker we
69  * keep this around. IN the future the creation of proxies is a task done on a
70  * container basis but the container as a logical representation. In other
71  * words, the container "Entity with RMI/IIOP" is not a container stack but
72  * an association at the invocation level that points to all metadata for
73  * a given container.
74  * <p/>
75  * In other words this is here for legacy reason and to not disrupt the
76  * container at once.
77  * In particular we declare that we "implement" the container invoker
78  * interface when we are just implementing the Proxy generation calls.
79  * Separation of concern.
80  * <p/>
81  * todo eliminate this class, at least in its present form.
82  *
83  * @author <a HREF="mailto:marc.fleury@jboss.org">Marc Fleury</a>
84  * @author <a HREF="mailto:scott.stark@jboss.org">Scott Stark/a>
85  * @author <a HREF="mailto:thomas.diesler@jboss.org">Thomas Diesler/a>
86  * @version $Revision: 37459 $
87  */

88 public class ProxyFactory
89    implements EJBProxyFactory
90 {
91    protected static final String JavaDoc HOME_INTERCEPTOR = "home";
92    protected static final String JavaDoc BEAN_INTERCEPTOR = "bean";
93    protected static final String JavaDoc LIST_ENTITY_INTERCEPTOR = "list-entity";
94
95    protected static Logger log = Logger.getLogger(ProxyFactory.class);
96
97    // Metadata for the proxies
98
public EJBMetaData JavaDoc ejbMetaData;
99
100    // as of EJB2.1, we may have the case of web-service enabled beans without
101
// remote interface, we will simply "mute" this factory in this case
102
protected boolean isServiceEndpointOnly;
103
104    protected EJBHome JavaDoc home;
105    protected EJBObject JavaDoc statelessObject;
106
107    // The name of the bean being deployed
108
protected String JavaDoc jndiBinding;
109    protected ObjectName JavaDoc jmxName;
110    protected int jmxNameHash;
111    private Integer JavaDoc jmxNameHashInteger;
112
113    // The name of the delegate invoker
114
// We have a beanInvoker and homeInvoker
115
// because clustering has a different invoker for each
116
// and we want to reuse code here.
117
protected Invoker beanInvoker;
118    protected Invoker homeInvoker;
119    protected InvokerProxyBindingMetaData invokerMetaData;
120
121    /**
122     * The proxy-config/client-interceptors/home stack
123     */

124    protected ArrayList JavaDoc homeInterceptorClasses = new ArrayList JavaDoc();
125    /**
126     * The proxy-config/client-interceptors/bean stack
127     */

128    protected ArrayList JavaDoc beanInterceptorClasses = new ArrayList JavaDoc();
129    /**
130     * The proxy-config/client-interceptors/entity-list stack
131     */

132    protected ArrayList JavaDoc listEntityInterceptorClasses = new ArrayList JavaDoc();
133    /** A flag indicating if the IClientContainer interface should be added */
134    protected boolean includeIClientIface;
135    // A pointer to the container this proxy factory is dedicated to
136
protected Container container;
137
138    protected Constructor JavaDoc proxyClassConstructor;
139
140
141    // Container plugin implementation -----------------------------------------
142

143    public void setContainer(Container con)
144    {
145       this.container = con;
146    }
147
148    public void setInvokerMetaData(InvokerProxyBindingMetaData metadata)
149    {
150       this.invokerMetaData = metadata;
151    }
152
153    public void setInvokerBinding(String JavaDoc binding)
154    {
155       this.jndiBinding = binding;
156    }
157
158    public void create() throws Exception JavaDoc
159    {
160       jmxName = container.getJmxName();
161       jmxNameHash = jmxName.hashCode();
162       jmxNameHashInteger = new Integer JavaDoc(jmxNameHash);
163       // Create metadata
164

165       BeanMetaData bmd = container.getBeanMetaData();
166       boolean isSession = !(bmd instanceof EntityMetaData);
167       boolean isStatelessSession = false;
168       if(isSession)
169       {
170          SessionMetaData smd = (SessionMetaData) bmd;
171          if(bmd.getRemote() == null)
172          {
173             isServiceEndpointOnly = true;
174             // nothing more to do
175
return;
176          }
177          isStatelessSession = smd.isStateless();
178       }
179       Class JavaDoc pkClass = null;
180       if(!isSession)
181       {
182          EntityMetaData metaData = (EntityMetaData) bmd;
183          String JavaDoc pkClassName = metaData.getPrimaryKeyClass();
184          try
185          {
186             if(pkClassName != null)
187             {
188                pkClass = container.getClassLoader().loadClass(pkClassName);
189             }
190             else
191             {
192                pkClass
193                   = container.getClassLoader()
194                   .loadClass(metaData.getEjbClass())
195                   .getField(metaData.getPrimKeyField())
196                   .getClass();
197             }
198          }
199          catch(NoSuchFieldException JavaDoc e)
200          {
201             log.error(
202                "Unable to identify Bean's Primary Key class!"
203                + " Did you specify a primary key class and/or field? Does that field exist?"
204             );
205             throw new RuntimeException JavaDoc("Primary Key Problem");
206          }
207          catch(NullPointerException JavaDoc e)
208          {
209             log.error(
210                "Unable to identify Bean's Primary Key class!"
211                + " Did you specify a primary key class and/or field? Does that field exist?"
212             );
213             throw new RuntimeException JavaDoc("Primary Key Problem");
214          }
215       }
216
217       ejbMetaData = new EJBMetaDataImpl(
218          ((EJBProxyFactoryContainer) container).getRemoteClass(),
219          ((EJBProxyFactoryContainer) container).getHomeClass(),
220          pkClass, //null if not entity
221
isSession, //Session
222
isStatelessSession, //Stateless
223
new HomeHandleImpl(jndiBinding)
224       );
225       log.debug("Proxy Factory for " + jndiBinding + " initialized");
226
227       initInterceptorClasses();
228    }
229
230    /**
231     * Become fully available. At this point our invokers should be started
232     * and we can bind the homes into JNDI.
233     */

234    public void start() throws Exception JavaDoc
235    {
236       if(!isServiceEndpointOnly)
237       {
238          setupInvokers();
239          bindProxy();
240       }
241    }
242
243    /**
244     * Lookup the invokers in the object registry. This typically cannot
245     * be done until our start method as the invokers may need to be started
246     * themselves.
247     */

248    protected void setupInvokers() throws Exception JavaDoc
249    {
250       ObjectName JavaDoc oname = new ObjectName JavaDoc(invokerMetaData.getInvokerMBean());
251       Invoker invoker = (Invoker) Registry.lookup(oname);
252       if(invoker == null)
253       {
254          throw new RuntimeException JavaDoc("invoker is null: " + oname);
255       }
256
257       homeInvoker = beanInvoker = invoker;
258    }
259
260
261    /**
262     * Load the client interceptor classes
263     */

264    protected void initInterceptorClasses() throws Exception JavaDoc
265    {
266       HashMap JavaDoc interceptors = new HashMap JavaDoc();
267
268       Element JavaDoc proxyConfig = invokerMetaData.getProxyFactoryConfig();
269       Element JavaDoc clientInterceptors = MetaData.getOptionalChild(
270          proxyConfig,
271          "client-interceptors", null
272       );
273       if(clientInterceptors != null)
274       {
275          String JavaDoc value = MetaData.getElementAttribute(clientInterceptors, "exposeContainer");
276          this.includeIClientIface = Boolean.valueOf(value).booleanValue();
277          NodeList JavaDoc children = clientInterceptors.getChildNodes();
278          for(int i = 0; i < children.getLength(); i++)
279          {
280             Node JavaDoc currentChild = children.item(i);
281             if(currentChild.getNodeType() == Node.ELEMENT_NODE)
282             {
283                Element JavaDoc interceptor = (Element JavaDoc) children.item(i);
284                interceptors.put(interceptor.getTagName(), interceptor);
285             }
286          }
287       }
288       else
289       {
290          log.debug("client interceptors element is null");
291       }
292       Element JavaDoc homeInterceptorConf = (Element JavaDoc) interceptors.get(HOME_INTERCEPTOR);
293       loadInterceptorClasses(homeInterceptorClasses, homeInterceptorConf);
294       if(homeInterceptorClasses.size() == 0)
295       {
296          throw new DeploymentException("There are no home interface interceptors configured");
297       }
298
299       Element JavaDoc beanInterceptorConf = (Element JavaDoc) interceptors.get(BEAN_INTERCEPTOR);
300       loadInterceptorClasses(beanInterceptorClasses, beanInterceptorConf);
301       if(beanInterceptorClasses.size() == 0)
302       {
303          throw new DeploymentException("There are no bean interface interceptors configured");
304       }
305
306       Element JavaDoc listEntityInterceptorConf = (Element JavaDoc) interceptors.get(LIST_ENTITY_INTERCEPTOR);
307       loadInterceptorClasses(listEntityInterceptorClasses, listEntityInterceptorConf);
308    }
309
310    /**
311     * The <code>loadInterceptorClasses</code> load an interceptor classes from
312     * configuration
313     *
314     * @throws Exception if an error occurs
315     */

316    protected void loadInterceptorClasses(ArrayList JavaDoc classes, Element JavaDoc interceptors)
317       throws Exception JavaDoc
318    {
319       Iterator JavaDoc interceptorElements = MetaData.getChildrenByTagName(interceptors, "interceptor");
320       ClassLoader JavaDoc loader = container.getClassLoader();
321       while(interceptorElements != null && interceptorElements.hasNext())
322       {
323          Element JavaDoc ielement = (Element JavaDoc) interceptorElements.next();
324          String JavaDoc className = null;
325          className = MetaData.getElementContent(ielement);
326
327          // load the invoker interceptor that corresponds to the beans call semantic
328
String JavaDoc byValueAttr = MetaData.getElementAttribute(ielement, "call-by-value");
329          if(byValueAttr != null)
330          {
331             if (container.isCallByValue() == new Boolean JavaDoc(byValueAttr).booleanValue())
332             {
333                Class JavaDoc clazz = loader.loadClass(className);
334                classes.add(clazz);
335             }
336          }
337          else
338          {
339             Class JavaDoc clazz = loader.loadClass(className);
340             classes.add(clazz);
341          }
342       }
343    }
344
345    /**
346     * The <code>loadInterceptorChain</code> create instances of interceptor
347     * classes previously loaded in loadInterceptorClasses
348     *
349     * @throws Exception if an error occurs
350     */

351    protected void loadInterceptorChain(ArrayList JavaDoc chain, ClientContainer client)
352       throws Exception JavaDoc
353    {
354       Interceptor last = null;
355       for(int i = 0; i < chain.size(); i++)
356       {
357          Class JavaDoc clazz = (Class JavaDoc) chain.get(i);
358          Interceptor interceptor = (Interceptor) clazz.newInstance();
359          if(last == null)
360          {
361             last = interceptor;
362             client.setNext(interceptor);
363          }
364          else
365          {
366             last.setNext(interceptor);
367             last = interceptor;
368          }
369       }
370    }
371
372    /**
373     * The <code>bindProxy</code> method creates the home proxy and binds
374     * the home into jndi. It also creates the InvocationContext and client
375     * container and interceptor chain.
376     *
377     * @throws Exception if an error occurs
378     */

379    protected void bindProxy() throws Exception JavaDoc
380    {
381       try
382       {
383          // Create a stack from the description (in the future) for now we hardcode it
384
InvocationContext context = new InvocationContext();
385
386          context.setObjectName(jmxNameHashInteger);
387          context.setValue(InvocationKey.JNDI_NAME, jndiBinding);
388          // The behavior for home proxying should be isolated in an interceptor FIXME
389
context.setInvoker(homeInvoker);
390          context.setValue(InvocationKey.EJB_METADATA, ejbMetaData);
391          context.setInvokerProxyBinding(invokerMetaData.getName());
392
393          ClientContainer client = null;
394          EJBProxyFactoryContainer pfc = (EJBProxyFactoryContainer) container;
395          Class JavaDoc[] ifaces = {pfc.getHomeClass(), Class.forName("javax.ejb.Handle")};
396          if( includeIClientIface )
397          {
398             ifaces = new Class JavaDoc[] {IClientContainer.class, pfc.getHomeClass(),
399                            Class.forName("javax.ejb.Handle")};
400             client = new ClientContainerEx(context);
401          }
402          else
403          {
404             client = new ClientContainer(context);
405          }
406          loadInterceptorChain(homeInterceptorClasses, client);
407
408          // Create the EJBHome
409
this.home = (EJBHome JavaDoc) Proxy.newProxyInstance(
410                // Class loader pointing to the right classes from deployment
411
pfc.getHomeClass().getClassLoader(),
412                // The classes we want to implement home and handle
413
ifaces,
414                // The home proxy as invocation handler
415
client);
416
417          // Create stateless session object
418
// Same instance is used for all objects
419
if(ejbMetaData.isStatelessSession() == true)
420          {
421             // Create a stack from the description (in the future) for now we hardcode it
422
context = new InvocationContext();
423
424             context.setObjectName(jmxNameHashInteger);
425             context.setValue(InvocationKey.JNDI_NAME, jndiBinding);
426             // The behavior for home proxying should be isolated in an interceptor FIXME
427
context.setInvoker(beanInvoker);
428             context.setInvokerProxyBinding(invokerMetaData.getName());
429             context.setValue(InvocationKey.EJB_HOME, home);
430
431             Class JavaDoc[] ssifaces = {pfc.getRemoteClass()};
432             if( includeIClientIface )
433             {
434                ssifaces = new Class JavaDoc[] {IClientContainer.class, pfc.getRemoteClass()};
435                client = new ClientContainerEx(context);
436             }
437             else
438             {
439                client = new ClientContainer(context);
440             }
441             loadInterceptorChain(beanInterceptorClasses, client);
442
443             this.statelessObject =
444                (EJBObject JavaDoc)Proxy.newProxyInstance(
445                   // Correct CL
446
pfc.getRemoteClass().getClassLoader(),
447                   // Interfaces
448
ssifaces,
449                   // SLSB proxy as invocation handler
450
client
451                );
452          }
453          else
454          {
455             // this is faster than newProxyInstance
456
Class JavaDoc[] intfs = {pfc.getRemoteClass()};
457             if( this.includeIClientIface )
458             {
459                intfs = new Class JavaDoc[]{IClientContainer.class, pfc.getRemoteClass()};
460             }
461             Class JavaDoc proxyClass = Proxy.getProxyClass(pfc.getRemoteClass().getClassLoader(), intfs);
462             final Class JavaDoc[] constructorParams = {InvocationHandler JavaDoc.class};
463             proxyClassConstructor = proxyClass.getConstructor(constructorParams);
464          }
465
466
467          // Bind the home in the JNDI naming space
468
rebindHomeProxy();
469       }
470       catch(Exception JavaDoc e)
471       {
472          throw new ServerException JavaDoc("Could not bind home", e);
473       }
474    }
475
476    protected void rebindHomeProxy() throws NamingException JavaDoc
477    {
478       // (Re-)Bind the home in the JNDI naming space
479
log.debug("(re-)Binding Home " + jndiBinding);
480       Util.rebind(
481          // The context
482
new InitialContext JavaDoc(),
483          // Jndi name
484
jndiBinding,
485          // The Home
486
getEJBHome()
487       );
488
489       log.info("Bound EJB Home '" + container.getBeanMetaData().getEjbName() + "' to jndi '" + jndiBinding + "'");
490    }
491
492    public void stop()
493    {
494    }
495
496    public void destroy()
497    {
498       if(!isServiceEndpointOnly)
499       {
500          log.info("Unbind EJB Home '" + container.getBeanMetaData().getEjbName() + "' from jndi '" + jndiBinding + "'");
501
502          try
503          {
504             InitialContext JavaDoc ctx = new InitialContext JavaDoc();
505             ctx.unbind(jndiBinding);
506          }
507          catch(Exception JavaDoc e)
508          {
509             // ignore.
510
}
511          homeInterceptorClasses.clear();
512          beanInterceptorClasses.clear();
513          listEntityInterceptorClasses.clear();
514       }
515
516       container = null;
517       ejbMetaData = null;
518       home = null;
519       statelessObject = null;
520       beanInvoker = null;
521       homeInvoker = null;
522       invokerMetaData = null;
523    }
524
525    // EJBProxyFactory implementation -------------------------------------
526

527    public boolean isIdentical(Container container, Invocation mi)
528    {
529       throw new UnsupportedOperationException JavaDoc("TODO provide a default implementation");
530    }
531
532    public EJBMetaData JavaDoc getEJBMetaData()
533    {
534       return ejbMetaData;
535    }
536
537    public Object JavaDoc getEJBHome()
538    {
539       return home;
540    }
541
542    /**
543     * Return the EJBObject proxy for stateless sessions.
544     */

545    public Object JavaDoc getStatelessSessionEJBObject()
546    {
547
548       return statelessObject;
549    }
550
551    /**
552     * Create an EJBObject proxy for a stateful session given its session id.
553     */

554    public Object JavaDoc getStatefulSessionEJBObject(Object JavaDoc id)
555    {
556       // Create a stack from the description (in the future) for now we hardcode it
557
InvocationContext context = new InvocationContext();
558
559       context.setObjectName(jmxNameHashInteger);
560       context.setCacheId(id);
561       context.setValue(InvocationKey.JNDI_NAME, jndiBinding);
562       context.setInvoker(beanInvoker);
563       log.debug("seting invoker proxy binding for stateful session: " + invokerMetaData.getName());
564       context.setInvokerProxyBinding(invokerMetaData.getName());
565       context.setValue(InvocationKey.EJB_HOME, home);
566       context.setValue("InvokerID", Invoker.ID);
567
568       ClientContainer client;
569       if( includeIClientIface )
570       {
571          client = new ClientContainerEx(context);
572       }
573       else
574       {
575          client = new ClientContainer(context);
576       }
577
578       try
579       {
580          loadInterceptorChain(beanInterceptorClasses, client);
581       }
582       catch(Exception JavaDoc e)
583       {
584          throw new NestedRuntimeException("Failed to load interceptor chain", e);
585       }
586
587       try
588       {
589          return (EJBObject JavaDoc) proxyClassConstructor.newInstance(new Object JavaDoc[]{client});
590       }
591       catch(Exception JavaDoc ex)
592       {
593          throw new NestedRuntimeException(ex);
594       }
595
596    }
597
598    /**
599     * Create an EJBObject proxy for an entity given its primary key.
600     */

601    public Object JavaDoc getEntityEJBObject(Object JavaDoc id)
602    {
603       Object JavaDoc result;
604       if(id == null)
605       {
606          result = null;
607       }
608       else
609       {
610          // Create a stack from the description (in the future) for now we hardcode it
611
InvocationContext context = new InvocationContext();
612
613          context.setObjectName(jmxNameHashInteger);
614          context.setCacheId(id);
615          context.setValue(InvocationKey.JNDI_NAME, jndiBinding);
616          context.setInvoker(beanInvoker);
617          context.setInvokerProxyBinding(invokerMetaData.getName());
618          context.setValue(InvocationKey.EJB_HOME, home);
619
620          ClientContainer client;
621          if( includeIClientIface )
622          {
623             client = new ClientContainerEx(context);
624          }
625          else
626          {
627             client = new ClientContainer(context);
628          }
629
630          try
631          {
632             loadInterceptorChain(beanInterceptorClasses, client);
633          }
634          catch(Exception JavaDoc e)
635          {
636             throw new NestedRuntimeException("Failed to load interceptor chain", e);
637          }
638
639          try
640          {
641             result = proxyClassConstructor.newInstance(new Object JavaDoc[]{client});
642          }
643          catch(Exception JavaDoc ex)
644          {
645             throw new NestedRuntimeException(ex);
646          }
647       }
648       return result;
649    }
650
651    /**
652     * Create a Collection EJBObject proxies for an entity given its primary keys.
653     */

654    public Collection JavaDoc getEntityCollection(Collection JavaDoc ids)
655    {
656       ArrayList JavaDoc list = new ArrayList JavaDoc(ids.size());
657       Iterator JavaDoc idEnum = ids.iterator();
658
659       while(idEnum.hasNext())
660       {
661          Object JavaDoc nextId = idEnum.next();
662          list.add(getEntityEJBObject(nextId));
663       }
664       return list;
665    }
666 }
667
Popular Tags