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.invocation.jrmp.server;
023:
024: import java.lang.reflect.Method;
025: import java.net.InetAddress;
026: import java.net.UnknownHostException;
027: import java.io.Serializable;
028: import java.rmi.server.RemoteServer;
029: import java.rmi.server.UnicastRemoteObject;
030: import java.rmi.server.RMIServerSocketFactory;
031: import java.rmi.server.RMIClientSocketFactory;
032: import java.rmi.server.RemoteStub;
033: import java.rmi.MarshalledObject;
034: import java.security.PrivilegedAction;
035: import java.security.AccessController;
036: import java.security.PrivilegedExceptionAction;
037: import java.security.PrivilegedActionException;
038:
039: import javax.management.ObjectName;
040: import javax.management.MBeanRegistration;
041: import javax.management.MBeanServer;
042: import javax.naming.Name;
043: import javax.naming.InitialContext;
044: import javax.naming.Context;
045: import javax.naming.NamingException;
046: import javax.naming.NameNotFoundException;
047: import javax.transaction.Transaction;
048:
049: import org.jboss.invocation.jrmp.interfaces.JRMPInvokerProxy;
050: import org.jboss.invocation.Invocation;
051: import org.jboss.invocation.Invoker;
052: import org.jboss.invocation.MarshalledInvocation;
053: import org.jboss.invocation.MarshalledValueInputStream;
054: import org.jboss.logging.Logger;
055: import org.jboss.mx.util.JMXExceptionDecoder;
056: import org.jboss.net.sockets.DefaultSocketFactory;
057: import org.jboss.security.SecurityDomain;
058: import org.jboss.system.Registry;
059: import org.jboss.system.ServiceMBeanSupport;
060: import org.jboss.tm.TransactionPropagationContextUtil;
061:
062: /**
063: * The JRMPInvoker is an RMI implementation that can generate Invocations
064: * from RMI/JRMP into the JMX base.
065: *
066: * @author <a href="mailto:marc.fleury@jboss.org>Marc Fleury</a>
067: * @author <a href="mailto:scott.stark@jboss.org>Scott Stark</a>
068: * @version $Revision: 60885 $
069: * @jmx.mbean extends="org.jboss.system.ServiceMBean"
070: */
071: public class JRMPInvoker extends RemoteServer implements Invoker,
072: JRMPInvokerMBean, MBeanRegistration {
073: /** @since 4.2.0 */
074: static final long serialVersionUID = 3110972460891691492L;
075:
076: /**
077: * Identifer to instruct the usage of an anonymous port.
078: */
079: public static final int ANONYMOUS_PORT = 0;
080:
081: /**
082: * Instance logger.
083: */
084: protected Logger log;
085:
086: /**
087: * Service MBean support delegate.
088: */
089: protected ServiceMBeanSupport support;
090:
091: /**
092: * The port the container will be exported on
093: */
094: protected int rmiPort = ANONYMOUS_PORT;
095:
096: /**
097: * An optional custom client socket factory
098: */
099: protected RMIClientSocketFactory clientSocketFactory;
100:
101: /**
102: * An optional custom server socket factory
103: */
104: protected RMIServerSocketFactory serverSocketFactory;
105:
106: /**
107: * The class name of the optional custom client socket factory
108: */
109: protected String clientSocketFactoryName;
110:
111: /**
112: * The class name of the optional custom server socket factory
113: */
114: protected String serverSocketFactoryName;
115:
116: /**
117: * The address to bind the rmi port on
118: */
119: protected String serverAddress;
120: /**
121: * The name of the security domain to use with server sockets that support SSL
122: */
123: protected String sslDomain;
124:
125: protected Serializable invokerStub;
126: /**
127: * The socket accept backlog
128: */
129: protected int backlog = 200;
130: /**
131: * A flag to enable caching of classes in the MarshalledValueInputStream
132: */
133: protected boolean enableClassCaching = false;
134: /**
135: * A priviledged actions for MBeanServer.invoke when running with sec mgr
136: */
137: private MBeanServerAction serverAction = new MBeanServerAction();
138:
139: public JRMPInvoker() {
140: final JRMPInvoker delegate = this ;
141:
142: // adapt the support delegate to invoke our state methods
143: support = new ServiceMBeanSupport(getClass()) {
144: protected void startService() throws Exception {
145: delegate.startService();
146: }
147:
148: protected void stopService() throws Exception {
149: delegate.stopService();
150: }
151:
152: protected void destroyService() throws Exception {
153: delegate.destroyService();
154: }
155: };
156:
157: // Setup logging from delegate
158: log = support.getLog();
159: }
160:
161: /**
162: * @jmx.managed-attribute
163: */
164: public int getBacklog() {
165: return backlog;
166: }
167:
168: /**
169: * @jmx.managed-attribute
170: */
171: public void setBacklog(int back) {
172: backlog = back;
173: }
174:
175: /**
176: * @jmx.managed-attribute
177: */
178: public boolean getEnableClassCaching() {
179: return enableClassCaching;
180: }
181:
182: /**
183: * @jmx.managed-attribute
184: */
185: public void setEnableClassCaching(boolean flag) {
186: enableClassCaching = flag;
187: MarshalledValueInputStream.useClassCache(enableClassCaching);
188: }
189:
190: /**
191: * @return The localhost name or null.
192: */
193: public String getServerHostName() {
194: try {
195: return InetAddress.getLocalHost().getHostName();
196: } catch (Exception ignored) {
197: return null;
198: }
199: }
200:
201: /**
202: * @jmx.managed-attribute
203: */
204: public void setRMIObjectPort(final int rmiPort) {
205: this .rmiPort = rmiPort;
206: }
207:
208: /**
209: * @jmx.managed-attribute
210: */
211: public int getRMIObjectPort() {
212: return rmiPort;
213: }
214:
215: /**
216: * @jmx.managed-attribute
217: */
218: public void setRMIClientSocketFactory(final String name) {
219: clientSocketFactoryName = name;
220: }
221:
222: /**
223: * @jmx.managed-attribute
224: */
225: public String getRMIClientSocketFactory() {
226: return clientSocketFactoryName;
227: }
228:
229: /**
230: * @jmx.managed-attribute
231: */
232: public void setRMIClientSocketFactoryBean(
233: final RMIClientSocketFactory bean) {
234: clientSocketFactory = bean;
235: }
236:
237: /**
238: * @jmx.managed-attribute
239: */
240: public RMIClientSocketFactory getRMIClientSocketFactoryBean() {
241: return clientSocketFactory;
242: }
243:
244: /**
245: * @jmx.managed-attribute
246: */
247: public void setRMIServerSocketFactory(final String name) {
248: serverSocketFactoryName = name;
249: }
250:
251: /**
252: * @jmx.managed-attribute
253: */
254: public String getRMIServerSocketFactory() {
255: return serverSocketFactoryName;
256: }
257:
258: /**
259: * @jmx.managed-attribute
260: */
261: public void setRMIServerSocketFactoryBean(
262: final RMIServerSocketFactory bean) {
263: serverSocketFactory = bean;
264: }
265:
266: /**
267: * @jmx.managed-attribute
268: */
269: public RMIServerSocketFactory getRMIServerSocketFactoryBean() {
270: return serverSocketFactory;
271: }
272:
273: /**
274: * @jmx.managed-attribute
275: */
276: public void setServerAddress(final String address) {
277: serverAddress = address;
278: }
279:
280: /**
281: * @jmx.managed-attribute
282: */
283: public String getServerAddress() {
284: return serverAddress;
285: }
286:
287: /**
288: * @jmx.managed-attribute
289: */
290: public void setSecurityDomain(String domainName) {
291: this .sslDomain = domainName;
292: }
293:
294: /**
295: * @jmx.managed-attribute
296: */
297: public String getSecurityDomain() {
298: return sslDomain;
299: }
300:
301: public Serializable getStub() {
302: return this .invokerStub;
303: }
304:
305: protected void startService() throws Exception {
306: loadCustomSocketFactories();
307:
308: if (log.isDebugEnabled()) {
309: log.debug("RMI Port='"
310: + (rmiPort == ANONYMOUS_PORT ? "Anonymous"
311: : Integer.toString(rmiPort)) + "'");
312:
313: log.debug("Client SocketFactory='"
314: + (clientSocketFactory == null ? "Default"
315: : clientSocketFactory.toString()) + "'");
316:
317: log.debug("Server SocketFactory='"
318: + (serverSocketFactory == null ? "Default"
319: : serverSocketFactory.toString()) + "'");
320:
321: log.debug("Server SocketAddr='"
322: + (serverAddress == null ? "Default"
323: : serverAddress) + "'");
324: log
325: .debug("SecurityDomain='"
326: + (sslDomain == null ? "Default"
327: : sslDomain) + "'");
328: }
329:
330: InitialContext ctx = new InitialContext();
331:
332: // Validate that there is a TransactionPropagationContextImporter
333: // bound in JNDI
334: TransactionPropagationContextUtil.getTPCImporter();
335:
336: // Set the transaction manager and transaction propagation
337: // context factory of the GenericProxy class
338:
339: Invoker delegateInvoker = createDelegateInvoker();
340:
341: // Make the remote invoker proxy available for use by the proxy factory
342: Registry.bind(support.getServiceName(), delegateInvoker);
343:
344: // Export CI
345: exportCI();
346:
347: log.debug("Bound JRMP invoker for JMX node");
348:
349: ctx.close();
350: }
351:
352: protected void stopService() throws Exception {
353: InitialContext ctx = new InitialContext();
354:
355: try {
356: unexportCI();
357: } finally {
358: ctx.close();
359: }
360: this .clientSocketFactory = null;
361: this .serverSocketFactory = null;
362: this .invokerStub = null;
363: }
364:
365: protected void destroyService() throws Exception {
366: // Export references to the bean
367: Registry.unbind(support.getServiceName());
368: }
369:
370: /**
371: * Invoke a Remote interface method.
372: */
373: public Object invoke(Invocation invocation) throws Exception {
374: ClassLoader oldCl = TCLAction.UTIL.getContextClassLoader();
375: ObjectName mbean = null;
376: try {
377: // Deserialize the transaction if it is there
378: MarshalledInvocation mi = (MarshalledInvocation) invocation;
379: invocation.setTransaction(importTPC(mi
380: .getTransactionPropagationContext()));
381:
382: mbean = (ObjectName) Registry.lookup(invocation
383: .getObjectName());
384:
385: // The cl on the thread should be set in another interceptor
386: Object obj = serverAction.invoke(mbean, "invoke",
387: new Object[] { invocation },
388: Invocation.INVOKE_SIGNATURE);
389: return new MarshalledObject(obj);
390: } catch (Exception e) {
391: Throwable th = JMXExceptionDecoder.decode(e);
392: if (log.isTraceEnabled())
393: log.trace("Failed to invoke on mbean: " + mbean, th);
394:
395: if (th instanceof Exception)
396: e = (Exception) th;
397:
398: throw e;
399: } finally {
400: TCLAction.UTIL.setContextClassLoader(oldCl);
401: Thread.interrupted(); // clear interruption because this thread may be pooled.
402: }
403: }
404:
405: protected Invoker createDelegateInvoker() {
406: return new JRMPInvokerProxy(this );
407: }
408:
409: protected void exportCI() throws Exception {
410: this .invokerStub = (Serializable) UnicastRemoteObject
411: .exportObject(this , rmiPort, clientSocketFactory,
412: serverSocketFactory);
413: }
414:
415: protected void unexportCI() throws Exception {
416: UnicastRemoteObject.unexportObject(this , true);
417: }
418:
419: protected void rebind(Context ctx, String name, Object val)
420: throws NamingException {
421: // Bind val to name in ctx, and make sure that all
422: // intermediate contexts exist
423:
424: Name n = ctx.getNameParser("").parse(name);
425: while (n.size() > 1) {
426: String ctxName = n.get(0);
427: try {
428: ctx = (Context) ctx.lookup(ctxName);
429: } catch (NameNotFoundException e) {
430: ctx = ctx.createSubcontext(ctxName);
431: }
432: n = n.getSuffix(1);
433: }
434:
435: ctx.rebind(n.get(0), val);
436: }
437:
438: /**
439: * Load and instantiate the clientSocketFactory, serverSocketFactory using
440: * the TCL and set the bind address and SSL domain if the serverSocketFactory
441: * supports it.
442: */
443: protected void loadCustomSocketFactories() {
444: ClassLoader loader = TCLAction.UTIL.getContextClassLoader();
445:
446: if (clientSocketFactory == null) {
447: try {
448: if (clientSocketFactoryName != null) {
449: Class csfClass = loader
450: .loadClass(clientSocketFactoryName);
451: clientSocketFactory = (RMIClientSocketFactory) csfClass
452: .newInstance();
453: }
454: } catch (Exception e) {
455: log.error("Failed to load client socket factory", e);
456: clientSocketFactory = null;
457: }
458: }
459:
460: if (serverSocketFactory == null) {
461: try {
462: if (serverSocketFactoryName != null) {
463: Class ssfClass = loader
464: .loadClass(serverSocketFactoryName);
465: serverSocketFactory = (RMIServerSocketFactory) ssfClass
466: .newInstance();
467: if (serverAddress != null) {
468: // See if the server socket supports setBindAddress(String)
469: try {
470: Class[] parameterTypes = { String.class };
471: Method m = ssfClass.getMethod(
472: "setBindAddress", parameterTypes);
473: Object[] args = { serverAddress };
474: m.invoke(serverSocketFactory, args);
475: } catch (NoSuchMethodException e) {
476: log
477: .warn("Socket factory does not support setBindAddress(String)");
478: // Go with default address
479: } catch (Exception e) {
480: log.warn("Failed to setBindAddress="
481: + serverAddress
482: + " on socket factory", e);
483: // Go with default address
484: }
485: }
486: /* See if the server socket supports setSecurityDomain(SecurityDomain)
487: if an sslDomain was specified
488: */
489: if (sslDomain != null) {
490: try {
491: InitialContext ctx = new InitialContext();
492: SecurityDomain domain = (SecurityDomain) ctx
493: .lookup(sslDomain);
494: Class[] parameterTypes = { SecurityDomain.class };
495: Method m = ssfClass
496: .getMethod("setSecurityDomain",
497: parameterTypes);
498: Object[] args = { domain };
499: m.invoke(serverSocketFactory, args);
500: } catch (NoSuchMethodException e) {
501: log
502: .error("Socket factory does not support setSecurityDomain(SecurityDomain)");
503: } catch (Exception e) {
504: log.error("Failed to setSecurityDomain="
505: + sslDomain + " on socket factory",
506: e);
507: }
508: }
509: }
510: // If a bind address was specified create a DefaultSocketFactory
511: else if (serverAddress != null) {
512: DefaultSocketFactory defaultFactory = new DefaultSocketFactory(
513: backlog);
514: serverSocketFactory = defaultFactory;
515: try {
516: defaultFactory.setBindAddress(serverAddress);
517: } catch (UnknownHostException e) {
518: log.error("Failed to setBindAddress="
519: + serverAddress + " on socket factory",
520: e);
521: }
522: }
523: } catch (Exception e) {
524: log.error("operation failed", e);
525: serverSocketFactory = null;
526: }
527: }
528: }
529:
530: /**
531: * Import a transaction propagation context into the local VM, and
532: * return the corresponding <code>Transaction</code>.
533: *
534: * @return A transaction or null if no tpc.
535: */
536: protected Transaction importTPC(Object tpc) {
537: if (tpc != null)
538: return TransactionPropagationContextUtil.importTPC(tpc);
539: return null;
540: }
541:
542: //
543: // Delegate the ServiceMBean details to our support delegate
544: //
545:
546: public String getName() {
547: return support.getName();
548: }
549:
550: public MBeanServer getServer() {
551: return support.getServer();
552: }
553:
554: public int getState() {
555: return support.getState();
556: }
557:
558: public String getStateString() {
559: return support.getStateString();
560: }
561:
562: public void create() throws Exception {
563: support.create();
564: }
565:
566: public void start() throws Exception {
567: support.start();
568: }
569:
570: public void stop() {
571: support.stop();
572: }
573:
574: public void destroy() {
575: support.destroy();
576: }
577:
578: public void jbossInternalLifecycle(String method) throws Exception {
579: support.jbossInternalLifecycle(method);
580: }
581:
582: public ObjectName preRegister(MBeanServer server, ObjectName name)
583: throws Exception {
584: return support.preRegister(server, name);
585: }
586:
587: public void postRegister(Boolean registrationDone) {
588: support.postRegister(registrationDone);
589: }
590:
591: public void preDeregister() throws Exception {
592: support.preDeregister();
593: }
594:
595: public void postDeregister() {
596: support.postDeregister();
597: }
598:
599: interface TCLAction {
600: class UTIL {
601: static TCLAction getTCLAction() {
602: return System.getSecurityManager() == null ? NON_PRIVILEGED
603: : PRIVILEGED;
604: }
605:
606: static ClassLoader getContextClassLoader() {
607: return getTCLAction().getContextClassLoader();
608: }
609:
610: static ClassLoader getContextClassLoader(Thread thread) {
611: return getTCLAction().getContextClassLoader(thread);
612: }
613:
614: static void setContextClassLoader(ClassLoader cl) {
615: getTCLAction().setContextClassLoader(cl);
616: }
617:
618: static void setContextClassLoader(Thread thread,
619: ClassLoader cl) {
620: getTCLAction().setContextClassLoader(thread, cl);
621: }
622: }
623:
624: TCLAction NON_PRIVILEGED = new TCLAction() {
625: public ClassLoader getContextClassLoader() {
626: return Thread.currentThread().getContextClassLoader();
627: }
628:
629: public ClassLoader getContextClassLoader(Thread thread) {
630: return thread.getContextClassLoader();
631: }
632:
633: public void setContextClassLoader(ClassLoader cl) {
634: Thread.currentThread().setContextClassLoader(cl);
635: }
636:
637: public void setContextClassLoader(Thread thread,
638: ClassLoader cl) {
639: thread.setContextClassLoader(cl);
640: }
641: };
642:
643: TCLAction PRIVILEGED = new TCLAction() {
644: private final PrivilegedAction getTCLPrivilegedAction = new PrivilegedAction() {
645: public Object run() {
646: return Thread.currentThread()
647: .getContextClassLoader();
648: }
649: };
650:
651: public ClassLoader getContextClassLoader() {
652: return (ClassLoader) AccessController
653: .doPrivileged(getTCLPrivilegedAction);
654: }
655:
656: public ClassLoader getContextClassLoader(final Thread thread) {
657: return (ClassLoader) AccessController
658: .doPrivileged(new PrivilegedAction() {
659: public Object run() {
660: return thread.getContextClassLoader();
661: }
662: });
663: }
664:
665: public void setContextClassLoader(final ClassLoader cl) {
666: AccessController.doPrivileged(new PrivilegedAction() {
667: public Object run() {
668: Thread.currentThread()
669: .setContextClassLoader(cl);
670: return null;
671: }
672: });
673: }
674:
675: public void setContextClassLoader(final Thread thread,
676: final ClassLoader cl) {
677: AccessController.doPrivileged(new PrivilegedAction() {
678: public Object run() {
679: thread.setContextClassLoader(cl);
680: return null;
681: }
682: });
683: }
684: };
685:
686: ClassLoader getContextClassLoader();
687:
688: ClassLoader getContextClassLoader(Thread thread);
689:
690: void setContextClassLoader(ClassLoader cl);
691:
692: void setContextClassLoader(Thread thread, ClassLoader cl);
693: }
694:
695: /**
696: * Perform the MBeanServer.invoke op in a PrivilegedExceptionAction if
697: * running with a security manager.
698: */
699: class MBeanServerAction implements PrivilegedExceptionAction {
700: private ObjectName target;
701: String method;
702: Object[] args;
703: String[] sig;
704:
705: MBeanServerAction() {
706: }
707:
708: MBeanServerAction(ObjectName target, String method,
709: Object[] args, String[] sig) {
710: this .target = target;
711: this .method = method;
712: this .args = args;
713: this .sig = sig;
714: }
715:
716: public Object run() throws Exception {
717: Object rtnValue = support.getServer().invoke(target,
718: method, args, sig);
719: return rtnValue;
720: }
721:
722: Object invoke(ObjectName target, String method, Object[] args,
723: String[] sig) throws Exception {
724: SecurityManager sm = System.getSecurityManager();
725: Object rtnValue = null;
726: if (sm == null) {
727: // Direct invocation on MBeanServer
728: rtnValue = support.getServer().invoke(target, method,
729: args, sig);
730: } else {
731: try {
732: // Encapsulate the invocation in a PrivilegedExceptionAction
733: MBeanServerAction action = new MBeanServerAction(
734: target, method, args, sig);
735: rtnValue = AccessController.doPrivileged(action);
736: } catch (PrivilegedActionException e) {
737: Exception ex = e.getException();
738: throw ex;
739: }
740: }
741: return rtnValue;
742: }
743: }
744: }
|