Save This Page
Home » jboss-5.0.0.CR1-src » org » jboss » naming » [javadoc | source]
    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.naming;
   23   
   24   import java.io.IOException;
   25   import java.io.InputStream;
   26   import java.io.Serializable;
   27   import java.lang.reflect.Constructor;
   28   import java.lang.reflect.InvocationHandler;
   29   import java.lang.reflect.InvocationTargetException;
   30   import java.lang.reflect.Method;
   31   import java.lang.reflect.Proxy;
   32   import java.net.URL;
   33   import java.util.Hashtable;
   34   import java.util.Properties;
   35   
   36   import javax.naming.CompositeName;
   37   import javax.naming.Context;
   38   import javax.naming.InitialContext;
   39   import javax.naming.Name;
   40   import javax.naming.NamingException;
   41   import javax.naming.RefAddr;
   42   import javax.naming.Reference;
   43   import javax.naming.Referenceable;
   44   import javax.naming.ldap.Control;
   45   import javax.naming.spi.ObjectFactory;
   46   
   47   import org.jboss.system.ServiceMBeanSupport;
   48   import org.jboss.util.Classes;
   49   
   50   /**
   51    * A MBean that binds an arbitrary InitialContext into the JBoss default
   52    * InitialContext as a Reference. If RemoteAccess is enabled, the reference
   53    * is a Serializable object that is capable of creating the InitialContext
   54    * remotely. If RemoteAccess if false, the reference is to a nonserializable object
   55    * that can only be used from within this VM.
   56    *
   57    * @jmx:mbean extends="org.jboss.system.ServiceMBean"
   58    * 
   59    * @see org.jboss.naming.NonSerializableFactory
   60    * 
   61    * @version <tt>$Revision: 70435 $</tt>
   62    * @author  Scott.Stark@jboss.org
   63    * @author  <a href="mailto:jason@planet57.com">Jason Dillon</a>
   64    */
   65   public class ExternalContext
   66      extends ServiceMBeanSupport
   67      implements ExternalContextMBean
   68   {
   69      private boolean remoteAccess;
   70      private SerializableInitialContext contextInfo = new SerializableInitialContext();
   71   
   72      /**
   73       * No-args constructor for JMX.
   74       */
   75      public ExternalContext()
   76      {
   77         super();
   78      }
   79   
   80      public ExternalContext(String jndiName, String contextPropsURL)
   81         throws IOException, NamingException
   82      {   
   83         setJndiName(jndiName);
   84         setPropertiesURL(contextPropsURL);
   85      }
   86   
   87      /**
   88       * Set the jndi name under which the external context is bound.
   89       *
   90       * @jmx:managed-attribute
   91       */
   92      public String getJndiName()
   93      {
   94         return contextInfo.getJndiName();
   95      }
   96   
   97      /**
   98       * Set the jndi name under which the external context is bound.
   99       *
  100       * @jmx:managed-attribute
  101       */
  102      public void setJndiName(String jndiName) throws NamingException
  103      {
  104         contextInfo.setJndiName(jndiName);
  105         if( super.getState() == STARTED )
  106         {
  107            unbind(jndiName);
  108            try
  109            {
  110               rebind();
  111            }
  112            catch(Exception e)
  113            {
  114               NamingException ne = new NamingException("Failed to update jndiName");
  115               ne.setRootCause(e);
  116               throw ne;
  117            }
  118         }
  119      }
  120   
  121      /**
  122       * @jmx:managed-attribute
  123       */
  124      public boolean getRemoteAccess()
  125      {
  126         return remoteAccess;
  127      }
  128      
  129      /**
  130       * @jmx:managed-attribute
  131       */
  132      public void setRemoteAccess(final boolean remoteAccess)
  133      {
  134         this.remoteAccess = remoteAccess;
  135      }
  136      
  137      /**
  138       * @jmx:managed-attribute
  139       */
  140      public boolean getCacheContext()
  141      {
  142         return contextInfo.getCacheContext();
  143      }
  144      
  145      /**
  146       * @jmx:managed-attribute
  147       */
  148      public void setCacheContext(boolean cacheContext)
  149      {
  150         contextInfo.setCacheContext(cacheContext);
  151      }
  152   
  153      /**
  154       * Get the class name of the InitialContext implementation to
  155       * use. Should be one of:
  156       * <ul>
  157       *   <li>javax.naming.InitialContext
  158       *   <li>javax.naming.directory.InitialDirContext
  159       *   <li>javax.naming.ldap.InitialLdapContext
  160       * </ul>
  161       *
  162       * @jmx:managed-attribute
  163       * 
  164       * @return the classname of the InitialContext to use
  165       */
  166      public String getInitialContext()
  167      {
  168         return contextInfo.getInitialContext();
  169      }
  170   
  171      /**
  172       * Set the class name of the InitialContext implementation to
  173       * use. Should be one of:
  174       * <ul>
  175       *   <li>javax.naming.InitialContext
  176       *   <li>javax.naming.directory.InitialDirContext
  177       *   <li>javax.naming.ldap.InitialLdapContext
  178       * </ul>
  179       *
  180       * @jmx:managed-attribute
  181       *
  182       * @param contextClass, the classname of the InitialContext to use
  183       */
  184      public void setInitialContext(String className) throws ClassNotFoundException
  185      {
  186         contextInfo.loadClass(className);
  187      }
  188   
  189      /**
  190       * Set the InitialContex class environment properties from the given URL.
  191       *
  192       * @jmx:managed-attribute
  193       */
  194      public void setPropertiesURL(String contextPropsURL) throws IOException
  195      {
  196         contextInfo.loadProperties(contextPropsURL);
  197      }
  198   
  199      /**
  200       * Set the InitialContex class environment properties.
  201       *
  202       * @jmx:managed-attribute
  203       */
  204      public void setProperties(final Properties props) throws IOException
  205      {
  206         contextInfo.setProperties(props);
  207      }
  208   
  209      /**
  210       * Get the InitialContex class environment properties.
  211       *
  212       * @jmx:managed-attribute
  213       */
  214      public Properties getProperties() throws IOException
  215      {
  216         return contextInfo.getProperties();
  217      }
  218      
  219      /**
  220       * Start the service by binding the external context into the
  221       * JBoss InitialContext.
  222       */
  223      protected void startService() throws Exception
  224      {
  225         rebind();
  226      }
  227   
  228      /**
  229       * Stop the service by unbinding the external context into the
  230       * JBoss InitialContext.
  231       */
  232      protected void stopService() throws Exception
  233      {
  234         if( contextInfo.getCacheContext() )
  235            unbind(contextInfo.getJndiName());
  236      }
  237   
  238      private static Context createContext(Context rootContext, Name name) throws NamingException
  239      {
  240         Context subctx = rootContext;
  241         for(int n = 0; n < name.size(); n ++)
  242         {
  243            String atom = name.get(n);
  244            try
  245            {
  246               Object obj = subctx.lookup(atom);
  247               subctx = (Context) obj;
  248            }
  249            catch(NamingException e)
  250            {
  251               // No binding exists, create a subcontext
  252               subctx = subctx.createSubcontext(atom);
  253            }
  254         }
  255   
  256         return subctx;
  257      }
  258   
  259      private void rebind() throws Exception
  260      {
  261         Context ctx = contextInfo.newContext();
  262         Context rootCtx = (Context) new InitialContext();
  263   
  264         log.debug("ctx="+ctx+", env="+ctx.getEnvironment());
  265         
  266         // Get the parent context into which we are to bind
  267         String jndiName = contextInfo.getJndiName();
  268         Name fullName = rootCtx.getNameParser("").parse(jndiName);
  269   
  270         log.debug("fullName="+fullName);
  271         
  272         Name parentName = fullName;
  273         if( fullName.size() > 1 )
  274            parentName = fullName.getPrefix(fullName.size()-1);
  275         else
  276            parentName = new CompositeName();
  277         
  278         log.debug("parentName="+parentName);
  279         
  280         Context parentCtx = createContext(rootCtx, parentName);
  281         
  282         log.debug("parentCtx="+parentCtx);
  283         
  284         Name atomName = fullName.getSuffix(fullName.size()-1);
  285         String atom = atomName.get(0);
  286         boolean cacheContext = contextInfo.getCacheContext();
  287         
  288         if( remoteAccess == true )
  289         {
  290            // Bind contextInfo as a Referenceable
  291            parentCtx.rebind(atom, contextInfo);
  292   
  293            // Cache the context using NonSerializableFactory to avoid creating
  294            // more than one context for in VM lookups
  295            if( cacheContext == true )
  296            {
  297               // If cacheContext is true we need to wrap the Context in a
  298               // proxy that allows the user to issue close on the lookup
  299               // Context without closing the inmemory Context.
  300               ctx = CachedContext.createProxyContext(ctx);
  301               NonSerializableFactory.rebind(jndiName, ctx);
  302            }
  303         }
  304         else if( cacheContext == true )
  305         {
  306            // Bind a reference to the extern context using
  307            // NonSerializableFactory as the ObjectFactory. The Context must
  308            // be wrapped in a proxy that allows the user to issue close on the
  309            // lookup Context without closing the inmemory Context.
  310   
  311            Context proxyCtx = CachedContext.createProxyContext(ctx);
  312            NonSerializableFactory.rebind(rootCtx, jndiName, proxyCtx);
  313         }
  314         else
  315         {
  316            // Bind the contextInfo so that each lookup results in the creation
  317            // of a new Context object. The returned Context must be closed
  318            // by the user to prevent resource leaks.
  319   
  320            parentCtx.rebind(atom, contextInfo);
  321         }
  322      }
  323   
  324      private void unbind(String jndiName)
  325      {
  326         try
  327         {
  328            Context rootCtx = new InitialContext();
  329            Context ctx = (Context) rootCtx.lookup(jndiName);
  330            if( ctx != null )
  331               ctx.close();
  332            rootCtx.unbind(jndiName);
  333            NonSerializableFactory.unbind(jndiName);
  334         }
  335         catch(NamingException e)
  336         {
  337            log.error("unbind failed", e);
  338         }
  339      }
  340   
  341      /**
  342       * The external InitialContext information class. It acts as the
  343       * RefAddr and ObjectFactory for the external IntialContext and can
  344       * be marshalled to a remote client.
  345       */
  346      public static class SerializableInitialContext
  347         extends RefAddr
  348         implements Referenceable, Serializable, ObjectFactory
  349      {
  350         private static final long serialVersionUID = -6512260531255770463L;
  351         private String jndiName;
  352         private Class contextClass = javax.naming.InitialContext.class;
  353         private Properties contextProps;
  354         private boolean cacheContext = true;
  355         private transient Context initialContext;
  356   
  357         public SerializableInitialContext()
  358         {
  359            this("SerializableInitialContext");
  360         }
  361         
  362         public SerializableInitialContext(String addrType)
  363         {
  364            super(addrType);
  365         }
  366   
  367         public String getJndiName()
  368         {
  369            return jndiName;
  370         }
  371         
  372         public void setJndiName(final String jndiName)
  373         {
  374            this.jndiName = jndiName;
  375         }
  376         
  377         public boolean getCacheContext()
  378         {
  379            return cacheContext;
  380         }
  381         
  382         public void setCacheContext(final boolean cacheContext)
  383         {
  384            this.cacheContext = cacheContext;
  385         }
  386         
  387         public String getInitialContext()
  388         {
  389            return contextClass.getName();
  390         }
  391         
  392         public void loadClass(String className) throws ClassNotFoundException
  393         {
  394            ClassLoader loader = Thread.currentThread().getContextClassLoader();
  395            contextClass = loader.loadClass(className);
  396         }
  397   
  398         public void setProperties(final Properties props)
  399         {
  400            contextProps = props;
  401         }
  402   
  403         public Properties getProperties()
  404         {
  405            return contextProps;
  406         }
  407         
  408         public void loadProperties(String contextPropsURL) throws IOException
  409         {
  410            InputStream is = null;
  411            contextProps = new Properties();
  412   
  413            // See if this is a URL we can load
  414            try
  415            {
  416               URL url = new URL(contextPropsURL);
  417               is = url.openStream();
  418               contextProps.load(is);
  419               return;
  420            }
  421            catch (IOException e)
  422            {   // Failed, try to locate a classpath resource below
  423               is = null;
  424            }
  425   
  426            is = Thread.currentThread().getContextClassLoader().getResourceAsStream(contextPropsURL);
  427            if( is == null )
  428            {
  429               throw new IOException("Failed to locate context props as URL or resource:"+contextPropsURL);
  430            }
  431            contextProps.load(is);
  432         }
  433   
  434         Context newContext() throws Exception
  435         {
  436            // First check the NonSerializableFactory cache
  437            initialContext = (Context) NonSerializableFactory.lookup(jndiName);
  438            // Create the context from the contextClass and contextProps
  439            if( initialContext == null )
  440               initialContext = newContext(contextClass, contextProps);
  441            return initialContext;
  442         }
  443   
  444         static Context newContext(Class contextClass, Properties contextProps)
  445            throws Exception
  446         {
  447            Context ctx = null;
  448            try
  449            {
  450               ctx = newDefaultContext(contextClass, contextProps);
  451            }
  452            catch(NoSuchMethodException e)
  453            {
  454               ctx = newLdapContext(contextClass, contextProps);
  455            }
  456            return ctx;
  457         }
  458         
  459         private static Context newDefaultContext(Class contextClass, Properties contextProps)
  460            throws Exception
  461         {
  462            Context ctx = null;
  463            Class[] types = {Hashtable.class};
  464            Constructor ctor = contextClass.getConstructor(types);
  465            Object[] args = {contextProps};
  466            ctx = (Context) ctor.newInstance(args);
  467            return ctx;
  468         }
  469         
  470         private static Context newLdapContext(Class contextClass, Properties contextProps)
  471            throws Exception
  472         {
  473            Context ctx = null;
  474            Class[] types = {Hashtable.class, Control[].class};
  475            Constructor ctor = contextClass.getConstructor(types);
  476            Object[] args = {contextProps, null};
  477            ctx = (Context) ctor.newInstance(args);
  478            return ctx;
  479         }
  480           
  481         public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment)
  482            throws Exception
  483         {
  484            Reference ref = (Reference) obj;
  485            SerializableInitialContext sic = (SerializableInitialContext) ref.get(0);
  486            return sic.newContext();
  487         }
  488           
  489         public Reference getReference() throws NamingException
  490         {
  491            Reference ref = new Reference(Context.class.getName(), this, this.getClass().getName(), null);
  492            return ref;
  493         }
  494   
  495         public Object getContent()
  496         {
  497            return null;
  498         }
  499      }
  500   
  501      /**
  502       * A proxy implementation of Context that simply intercepts the
  503       * close() method and ignores it since the underlying Context
  504       * object is being maintained in memory.
  505       */
  506      static class CachedContext implements InvocationHandler
  507      {
  508         Context externalCtx;
  509         
  510         CachedContext(Context externalCtx)
  511         {
  512            this.externalCtx = externalCtx;
  513         }
  514   
  515         static Context createProxyContext(Context ctx)
  516         {
  517            ClassLoader loader = Thread.currentThread().getContextClassLoader();
  518            Class[] interfaces = Classes.getAllUniqueInterfaces(ctx.getClass());
  519            InvocationHandler handler = new CachedContext(ctx);
  520            Context proxyCtx = (Context) Proxy.newProxyInstance(loader, interfaces, handler);
  521            return proxyCtx;
  522         }
  523   
  524         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
  525         {
  526            Object value = null;
  527            if( method.getName().equals("close") )
  528            {
  529               // We just ignore the close method 
  530            }
  531            else
  532            {
  533               try
  534               {
  535                  value = method.invoke(externalCtx, args);
  536               }
  537               catch(InvocationTargetException e)
  538               {
  539                  throw e.getTargetException();
  540               }
  541            }
  542            return value;
  543         }
  544      }
  545   }

Save This Page
Home » jboss-5.0.0.CR1-src » org » jboss » naming » [javadoc | source]