001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.ejb3.service;
023:
024: import org.jboss.annotation.ejb.Management;
025: import org.jboss.annotation.ejb.Service;
026: import org.jboss.aop.AspectManager;
027: import org.jboss.aop.MethodInfo;
028: import org.jboss.aop.advice.Interceptor;
029: import org.jboss.aop.joinpoint.Invocation;
030: import org.jboss.aop.joinpoint.InvocationResponse;
031: import org.jboss.aop.joinpoint.MethodInvocation;
032: import org.jboss.aop.util.MethodHashing;
033: import org.jboss.aop.util.PayloadKey;
034: import org.jboss.aspects.asynch.FutureHolder;
035: import org.jboss.ejb.AllowedOperationsAssociation;
036: import org.jboss.ejb.AllowedOperationsFlags;
037: import org.jboss.ejb3.BeanContext;
038: import org.jboss.ejb3.EJBContainerInvocation;
039: import org.jboss.ejb3.Ejb3Deployment;
040: import org.jboss.ejb3.ProxyFactory;
041: import org.jboss.ejb3.SessionContainer;
042: import org.jboss.ejb3.asynchronous.AsynchronousInterceptor;
043: import org.jboss.ejb3.interceptor.InterceptorInfoRepository;
044: import org.jboss.ejb3.timerservice.TimedObjectInvoker;
045: import org.jboss.ejb3.timerservice.TimerServiceFactory;
046: import org.jboss.injection.Injector;
047: import org.jboss.logging.Logger;
048:
049: import javax.ejb.EJBException;
050: import javax.ejb.Handle;
051: import javax.ejb.Timer;
052: import javax.ejb.TimerService;
053: import javax.management.Attribute;
054: import javax.management.AttributeList;
055: import javax.management.AttributeNotFoundException;
056: import javax.management.InstanceNotFoundException;
057: import javax.management.InvalidAttributeValueException;
058: import javax.management.MBeanException;
059: import javax.management.MBeanInfo;
060: import javax.management.MBeanRegistrationException;
061: import javax.management.MBeanServer;
062: import javax.management.ObjectName;
063: import javax.management.ReflectionException;
064: import java.lang.reflect.Method;
065: import java.util.Hashtable;
066:
067: /**
068: * @author <a href="mailto:kabir.khan@jboss.org">Kabir Khan</a>
069: * @version $Revision: 63290 $
070: */
071: public class ServiceContainer extends SessionContainer implements
072: TimedObjectInvoker {
073: ServiceMBeanDelegate delegate;
074: Object singleton;
075: boolean injected;
076: BeanContext beanContext;
077: MBeanServer mbeanServer;
078: ObjectName delegateObjectName;
079: private TimerService timerService;
080:
081: @SuppressWarnings("unused")
082: private static final Logger log = Logger
083: .getLogger(ServiceContainer.class);
084:
085: public ServiceContainer(MBeanServer server, ClassLoader cl,
086: String beanClassName, String ejbName,
087: AspectManager manager, Hashtable ctxProperties,
088: InterceptorInfoRepository interceptorRepository,
089: Ejb3Deployment deployment) {
090: super (cl, beanClassName, ejbName, manager, ctxProperties,
091: interceptorRepository, deployment);
092: beanContextClass = ServiceBeanContext.class;
093: this .mbeanServer = server;
094: }
095:
096: public void callTimeout(Timer timer) throws Exception {
097: Method timeout = callbackHandler.getTimeoutCallback();
098: if (timeout == null)
099: throw new EJBException(
100: "No method has been annotated with @Timeout");
101: Object[] args = { timer };
102: AllowedOperationsAssociation
103: .pushInMethodFlag(AllowedOperationsFlags.IN_EJB_TIMEOUT);
104: try {
105: localInvoke(timeout, args);
106: } catch (Throwable throwable) {
107: if (throwable instanceof Exception)
108: throw (Exception) throwable;
109: if (throwable instanceof Error)
110: throw (Error) throwable;
111: throw new RuntimeException(throwable);
112: } finally {
113: AllowedOperationsAssociation.popInMethodFlag();
114: }
115: }
116:
117: protected Object createSession(Class initTypes[], Object initArgs[]) {
118: // if((initTypes != null && initTypes.length > 0) || (initArgs != null && initArgs.length > 0))
119: // throw new IllegalArgumentException("service bean create method must take no arguments");
120: throw new RuntimeException("NYI");
121: }
122:
123: public Object getSingleton() {
124: return singleton;
125: }
126:
127: public void create() throws Exception {
128: super .create();
129:
130: // EJBTHREE-655: fire up an instance for use as MBean delegate
131: singleton = super .construct();
132:
133: // won't work, before starting the management interface MBean injection must have been done.
134: //registerManagementInterface();
135:
136: invokeOptionalMethod("create");
137: }
138:
139: public void start() throws Exception {
140: super .start();
141:
142: try {
143: initBeanContext();
144:
145: // make sure the timer service is there before injection takes place
146: timerService = TimerServiceFactory.getInstance()
147: .createTimerService(this .getObjectName(), this );
148:
149: injectDependencies(beanContext);
150:
151: // TODO: EJBTHREE-655: shouldn't happen here, but in create
152: registerManagementInterface();
153:
154: // only restore timers if this is a restart to avoid duplicate timers
155: // when the timer is created during lifecycle
156: if (timerService.getTimers().size() == 0)
157: TimerServiceFactory.getInstance().restoreTimerService(
158: timerService);
159:
160: invokeOptionalMethod("start");
161: } catch (Exception e) {
162: e.printStackTrace();
163: stop();
164: }
165: }
166:
167: public void stop() throws Exception {
168: invokeOptionalMethod("stop");
169:
170: if (timerService != null) {
171: TimerServiceFactory.getInstance().removeTimerService(
172: timerService);
173: timerService = null;
174: }
175:
176: // TODO: EJBTHREE-655: shouldn't happen here, but in destroy
177: unregisterManagementInterface();
178:
179: injected = false;
180:
181: super .stop();
182: }
183:
184: public void destroy() throws Exception {
185: invokeOptionalMethod("destroy");
186:
187: //unregisterManagementInterface();
188:
189: super .destroy();
190: }
191:
192: public void initializePool() throws Exception {
193: resolveInjectors();
194: }
195:
196: public TimerService getTimerService() {
197: return timerService;
198: }
199:
200: public TimerService getTimerService(Object pKey) {
201: assert timerService != null : "Timer Service not yet initialized";
202: return timerService;
203: }
204:
205: /**
206: * Invoke a method on the singleton without a specific security or transaction context.
207: *
208: * @param methodName
209: */
210: private void invokeOptionalMethod(String methodName) {
211: /* EJBTHREE-655 has been postponed
212: ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
213: try
214: {
215: Thread.currentThread().setContextClassLoader(classloader);
216: Class parameterTypes[] = { };
217: Method method = clazz.getMethod(methodName, parameterTypes);
218: Object args[] = { };
219: method.invoke(singleton, args);
220: }
221: catch(NoSuchMethodException e)
222: {
223: // ignore
224: }
225: catch (IllegalArgumentException e)
226: {
227: throw new RuntimeException(e);
228: }
229: catch (IllegalAccessException e)
230: {
231: throw new RuntimeException(e);
232: }
233: catch (InvocationTargetException e)
234: {
235: throw new RuntimeException(e.getCause());
236: }
237: finally
238: {
239: Thread.currentThread().setContextClassLoader(oldLoader);
240: }
241: */
242: }
243:
244: public void invokePostConstruct(BeanContext beanContext) {
245: //Ignore
246: }
247:
248: public void invokePreDestroy(BeanContext beanContext) {
249: //Ignore
250: }
251:
252: public void invokeInit(Object bean) {
253: //Ignore
254: }
255:
256: /**
257: * Performs a synchronous local invocation
258: */
259: public Object localInvoke(Method method, Object[] args)
260: throws Throwable {
261: return localInvoke(method, args, null);
262: }
263:
264: /**
265: * Performs a synchronous or asynchronous local invocation
266: *
267: * @param provider If null a synchronous invocation, otherwise an asynchronous
268: */
269: public Object localInvoke(Method method, Object[] args,
270: FutureHolder provider) throws Throwable {
271: long start = System.currentTimeMillis();
272:
273: ClassLoader oldLoader = Thread.currentThread()
274: .getContextClassLoader();
275: try {
276: invokeStats.callIn();
277:
278: Thread.currentThread().setContextClassLoader(classloader);
279: long hash = MethodHashing.calculateHash(method);
280: MethodInfo info = (MethodInfo) methodInterceptors.get(hash);
281: if (info == null) {
282: throw new RuntimeException(
283: "Could not resolve beanClass method from proxy call: "
284: + method.toString());
285: }
286: EJBContainerInvocation nextInvocation = new EJBContainerInvocation(
287: info);
288: nextInvocation.setAdvisor(this );
289: nextInvocation.setArguments(args);
290:
291: nextInvocation = populateInvocation(nextInvocation);
292:
293: if (provider != null) {
294: nextInvocation.getMetaData().addMetaData(
295: AsynchronousInterceptor.ASYNCH,
296: AsynchronousInterceptor.INVOKE_ASYNCH, "YES",
297: PayloadKey.AS_IS);
298: nextInvocation.getMetaData().addMetaData(
299: AsynchronousInterceptor.ASYNCH,
300: AsynchronousInterceptor.FUTURE_HOLDER,
301: provider, PayloadKey.AS_IS);
302: }
303: return nextInvocation.invokeNext();
304: } finally {
305: if (method != null) {
306: long end = System.currentTimeMillis();
307: long elapsed = end - start;
308: invokeStats.updateStats(method, elapsed);
309: }
310:
311: invokeStats.callOut();
312:
313: Thread.currentThread().setContextClassLoader(oldLoader);
314: }
315: }
316:
317: public InvocationResponse dynamicInvoke(Object target,
318: Invocation invocation) throws Throwable {
319: long start = System.currentTimeMillis();
320:
321: ClassLoader oldLoader = Thread.currentThread()
322: .getContextClassLoader();
323: EJBContainerInvocation newSi = null;
324:
325: MethodInvocation si = (MethodInvocation) invocation;
326: MethodInfo info = (MethodInfo) methodInterceptors.get(si
327: .getMethodHash());
328: Method method = info.getUnadvisedMethod();
329: try {
330: invokeStats.callIn();
331:
332: Thread.currentThread().setContextClassLoader(classloader);
333:
334: if (info == null) {
335: throw new RuntimeException(
336: "Could not resolve beanClass method from proxy call");
337: }
338: newSi = new EJBContainerInvocation(info);
339: newSi.setArguments(si.getArguments());
340: newSi.setMetaData(si.getMetaData());
341: newSi.setAdvisor(this );
342:
343: newSi = populateInvocation(newSi);
344:
345: Object rtn = null;
346: try {
347: rtn = newSi.invokeNext();
348: } catch (Throwable throwable) {
349: return marshallException(invocation, throwable, newSi
350: .getResponseContextInfo());
351: }
352: InvocationResponse response = SessionContainer
353: .marshallResponse(invocation, rtn, newSi
354: .getResponseContextInfo());
355:
356: return response;
357: } finally {
358: if (method != null) {
359: long end = System.currentTimeMillis();
360: long elapsed = end - start;
361: invokeStats.updateStats(method, elapsed);
362: }
363:
364: invokeStats.callOut();
365:
366: Thread.currentThread().setContextClassLoader(oldLoader);
367: }
368: }
369:
370: protected void initBeanContext() throws RuntimeException {
371: if (beanContext == null) {
372: synchronized (singleton) {
373: if (beanContext == null) {
374: try {
375: beanContext = (BeanContext) beanContextClass
376: .newInstance();
377: beanContext.setContainer(this );
378: beanContext.initialiseInterceptorInstances();
379: beanContext.setInstance(singleton);
380: } catch (InstantiationException e) {
381: throw new RuntimeException(e); //To change body of catch statement use Options | File Templates.
382: } catch (IllegalAccessException e) {
383: throw new RuntimeException(e); //To change body of catch statement use Options | File Templates.
384: }
385: }
386: }
387: }
388: }
389:
390: @Override
391: protected EJBContainerInvocation populateInvocation(
392: EJBContainerInvocation invocation) {
393: invocation.setTargetObject(singleton);
394: invocation.setBeanContext(beanContext);
395: return invocation;
396: }
397:
398: protected synchronized void injectDependencies(BeanContext ctx) {
399: if (injectors != null) {
400: try {
401: pushEnc();
402: for (Injector injector : injectors) {
403: injector.inject(ctx);
404: }
405: } finally {
406: popEnc();
407: }
408: }
409: injected = true;
410: }
411:
412: // Dynamic MBean implementation --------------------------------------------------
413:
414: public Object getAttribute(String attribute)
415: throws AttributeNotFoundException, MBeanException,
416: ReflectionException {
417: return delegate.getAttribute(attribute);
418: }
419:
420: public void setAttribute(Attribute attribute)
421: throws AttributeNotFoundException,
422: InvalidAttributeValueException, MBeanException,
423: ReflectionException {
424: delegate.setAttribute(attribute);
425: }
426:
427: public AttributeList getAttributes(String[] attributes) {
428: return delegate.getAttributes(attributes);
429: }
430:
431: public AttributeList setAttributes(AttributeList attributes) {
432: return delegate.setAttributes(attributes);
433: }
434:
435: public Object invoke(String actionName, Object params[],
436: String signature[]) throws MBeanException,
437: ReflectionException {
438: return delegate.invoke(actionName, params, signature);
439: }
440:
441: @Override
442: protected Object invokeEJBObjectMethod(ProxyFactory factory,
443: Object id, MethodInfo info, Object[] args) throws Exception {
444: throw new RuntimeException("NYI");
445: }
446:
447: public MBeanInfo getMBeanInfo() {
448: return delegate.getMBeanInfo();
449: }
450:
451: public Object createLocalProxy(Object id) throws Exception {
452: ServiceLocalProxyFactory factory = new ServiceLocalProxyFactory();
453: factory.setContainer(this );
454:
455: return factory.createProxy(id);
456: }
457:
458: public Object createRemoteProxy(Object id) throws Exception {
459: ServiceRemoteProxyFactory factory = new ServiceRemoteProxyFactory();
460: factory.setContainer(this );
461:
462: return factory.createProxy(id);
463: }
464:
465: private void registerManagementInterface() {
466: try {
467: Management annotation = (Management) resolveAnnotation(Management.class);
468:
469: Class intf = null;
470: if (annotation != null)
471: intf = annotation.value();
472:
473: if (intf == null) {
474: Class[] interfaces = this .getBeanClass()
475: .getInterfaces();
476: int interfaceIndex = 0;
477: while (intf == null
478: && interfaceIndex < interfaces.length) {
479: if (interfaces[interfaceIndex]
480: .getAnnotation(Management.class) != null)
481: intf = interfaces[interfaceIndex];
482: else
483: ++interfaceIndex;
484: }
485: }
486:
487: if (intf != null) {
488: if (mbeanServer == null)
489: mbeanServer = org.jboss.mx.util.MBeanServerLocator
490: .locateJBoss();
491:
492: if (mbeanServer == null)
493: throw new RuntimeException(
494: "There is a @Management interface on "
495: + ejbName
496: + " but the MBeanServer has not been initialized for it");
497:
498: Service service = (Service) resolveAnnotation(Service.class);
499:
500: String objname = service.objectName();
501: delegateObjectName = (objname == null || objname
502: .equals("")) ? new ObjectName(getObjectName()
503: .getCanonicalName()
504: + ",type=ManagementInterface")
505: : new ObjectName(service.objectName());
506:
507: delegate = new ServiceMBeanDelegate(mbeanServer, this ,
508: intf, delegateObjectName);
509:
510: getDeployment().getKernelAbstraction().installMBean(
511: delegateObjectName, getDependencyPolicy(),
512: delegate);
513: } else {
514: Service service = (Service) resolveAnnotation(Service.class);
515: if (service.xmbean().length() > 0) {
516: if (mbeanServer == null)
517: mbeanServer = org.jboss.mx.util.MBeanServerLocator
518: .locateJBoss();
519:
520: if (mbeanServer == null)
521: throw new RuntimeException(
522: ejbName
523: + "is defined as an XMBean, but the MBeanServer has not been initialized for it");
524:
525: String objname = service.objectName();
526: delegateObjectName = (objname == null || objname
527: .equals("")) ? new ObjectName(
528: getObjectName().getCanonicalName()
529: + ",type=ManagementInterface")
530: : new ObjectName(service.objectName());
531:
532: delegate = new ServiceMBeanDelegate(mbeanServer,
533: this , service.xmbean(), delegateObjectName);
534:
535: getDeployment().getKernelAbstraction()
536: .installMBean(delegateObjectName,
537: getDependencyPolicy(), delegate);
538: }
539: }
540:
541: } catch (Exception e) {
542: throw new RuntimeException(
543: "Problem registering @Management interface for @Service "
544: + getBeanClass(), e);
545: }
546: }
547:
548: private void unregisterManagementInterface()
549: throws InstanceNotFoundException,
550: MBeanRegistrationException {
551: if (delegate != null) {
552: getDeployment().getKernelAbstraction().uninstallMBean(
553: delegateObjectName);
554: }
555: }
556:
557: protected void removeHandle(Handle handle) {
558: throw new RuntimeException("Don't do this");
559: }
560: }
|