User: luke_t  
  Date: 02/02/03 13:10:50

  Modified:    src/main/org/jboss/security/plugins
                        JaasSecurityManagerService.java
  Log:
  added log warning msg if cache policy lookup in JNDI fails.
  Applied code formatting.
  
  Revision  Changes    Path
  1.10      +343 -323  
jbosssx/src/main/org/jboss/security/plugins/JaasSecurityManagerService.java
  
  Index: JaasSecurityManagerService.java
  ===================================================================
  RCS file: 
/cvsroot/jboss/jbosssx/src/main/org/jboss/security/plugins/JaasSecurityManagerService.java,v
  retrieving revision 1.9
  retrieving revision 1.10
  diff -u -r1.9 -r1.10
  --- JaasSecurityManagerService.java   2001/11/26 03:24:54     1.9
  +++ JaasSecurityManagerService.java   2002/02/03 21:10:50     1.10
  @@ -1,323 +1,343 @@
  -/*
  - * JBoss, the OpenSource EJB server
  - *
  - * Distributable under LGPL license.
  - * See terms of license at gnu.org.
  - */
  -
  -package org.jboss.security.plugins;
  -
  -import java.lang.reflect.Constructor;
  -import java.lang.reflect.InvocationHandler;
  -import java.lang.reflect.Method;
  -import java.lang.reflect.Proxy;
  -import java.util.Enumeration;
  -import java.util.Hashtable;
  -
  -import javax.naming.InitialContext;
  -import javax.naming.Context;
  -import javax.naming.Reference;
  -import javax.naming.RefAddr;
  -import javax.naming.StringRefAddr;
  -import javax.naming.Name;
  -import javax.naming.NameClassPair;
  -import javax.naming.NameParser;
  -import javax.naming.NamingEnumeration;
  -import javax.naming.NamingException;
  -import javax.naming.OperationNotSupportedException;
  -import javax.naming.spi.ObjectFactory;
  -import javax.naming.CommunicationException;
  -import javax.security.auth.Subject;
  -
  -import javax.management.MalformedObjectNameException;
  -import javax.management.MBeanServer;
  -import javax.management.ObjectName;
  -
  -import org.jboss.logging.Logger;
  -import org.jboss.security.SecurityAssociation;
  -import org.jboss.security.SecurityProxyFactory;
  -import org.jboss.security.SubjectSecurityManager;
  -import org.jboss.util.CachePolicy;
  -import org.jboss.system.ServiceMBeanSupport;
  -
  -/**
  - *   This is a JMX service which manages JAAS based SecurityManagers.
  - *    JAAS SecurityManagers are responsible for validating credentials
  - *    associated with principals. The service defaults to the
  - *    org.jboss.security.plugins.JaasSecurityManager implementation but
  - *    this can be changed via the securityManagerClass property.
  - *
  - *   @see JaasSecurityManager
  - *   @see SubjectSecurityManager
  - *   @author <a href="[EMAIL PROTECTED]">Oleg Nitz</a>
  - *   @author <a href="[EMAIL PROTECTED]">Rickard Oberg</a>
  - *   @author <a href="mailto:[EMAIL PROTECTED]";>Scott Stark</a>
  - */
  -public class JaasSecurityManagerService
  -   extends ServiceMBeanSupport
  -   implements JaasSecurityManagerServiceMBean
  -{
  -   /** The logging interface */
  -   private static Logger theLog;
  -   /** The class that provides the security manager implementation */
  -   private static String securityMgrClassName = 
"org.jboss.security.plugins.JaasSecurityManager";
  -   /** The loaded securityMgrClassName */
  -   private static Class securityMgrClass;
  -   /** The security credential cache policy, shared by all security mgrs */
  -   private static CachePolicy cachePolicy;
  -   private static String cacheJndiName;
  -   /** The class that provides the SecurityProxyFactory implementation */
  -   private static String securityProxyFactoryClassName = 
"org.jboss.security.SubjectSecurityProxyFactory";
  -   private static Class securityProxyFactoryClass = 
org.jboss.security.SubjectSecurityProxyFactory.class;
  -   
  -   private static Hashtable jsmMap = new Hashtable();
  -   private static NameParser parser;
  -
  -   static
  -   {
  -      // use thread-local principal and credential propagation
  -      SecurityAssociation.setServer();
  -      // Get a theLog interface
  -      theLog = Logger.getLogger(JaasSecurityManagerService.class);
  -   }
  -
  -   /** The constructor does nothing as the security manager is created
  -    on each lookup into java:/jaas/xxx. This is also why all variables
  -    in this class are static.
  -    */
  -   public JaasSecurityManagerService()
  -   {
  -   }
  -   
  -   public String getSecurityManagerClassName()
  -   {
  -      return securityMgrClassName;
  -   }
  -   public void setSecurityManagerClassName(String className)
  -      throws ClassNotFoundException
  -   {
  -      securityMgrClassName = className;
  -      ClassLoader loader = Thread.currentThread().getContextClassLoader();
  -      securityMgrClass = loader.loadClass(securityMgrClassName);
  -   }
  -   public String getSecurityProxyFactoryClassName()
  -   {
  -      return securityProxyFactoryClassName;
  -   }
  -   public void setSecurityProxyFactoryClassName(String className)
  -      throws ClassNotFoundException
  -   {
  -      securityProxyFactoryClassName = className;
  -      ClassLoader loader = Thread.currentThread().getContextClassLoader();
  -      securityProxyFactoryClass = loader.loadClass(securityProxyFactoryClassName);
  -   }
  -   /** Get the jndi name under which the authentication cache policy is found
  -    */
  -   public String getAuthenticationCacheJndiName()
  -   {
  -      return cacheJndiName;
  -   }
  -   /** Set the jndi name under which the authentication cache policy is found
  -    */
  -   public void setAuthenticationCacheJndiName(String jndiName)
  -   {
  -      this.cacheJndiName = jndiName;
  -   }
  -   /** flush the cache policy for the indicated security domain if the security 
manager
  -    * instance supports a flushCache() method.
  -    */
  -   public void flushAuthenticationCache(String securityDomain)
  -   {
  -        String jndiName = "java:/jaas/" + securityDomain;
  -        try
  -        {
  -           InitialContext ic = new InitialContext();
  -           Object securityMgr = ic.lookup(jndiName);
  -           Class clazz = securityMgr.getClass();
  -           Class[] argTypes = {};
  -           Method flush = clazz.getMethod("flushCache", argTypes);
  -           Object[] args = {};
  -           flush.invoke(securityMgr, args);
  -        }
  -        catch(Exception e)
  -        {
  -            theLog.error("flushAuthenticationCache failed", e);
  -        }
  -   }
  -
  -   public String getName()
  -   {
  -      return "JAAS Security Manager";
  -   }
  -
  -   protected ObjectName getObjectName(MBeanServer server, ObjectName name)
  -      throws MalformedObjectNameException
  -   {
  -      return new ObjectName(OBJECT_NAME);
  -   }
  -   
  -   protected void startService() throws Exception
  -   {     
  -      InitialContext ic = new InitialContext();
  -
  -      // Bind reference to SM subcontext in JNDI
  -      // Uses JNDI federation to handle the "java:/jaas" context ourselves
  -      RefAddr refAddr = new StringRefAddr("nns", "JSM");
  -      String factoryName = SecurityDomainObjectFactory.class.getName();
  -      Reference jsmsRef = new Reference("javax.naming.Context", refAddr, 
factoryName, null);
  -      Context ctx = new InitialContext();
  -      ctx.rebind("java:/jaas", jsmsRef);
  -      parser = ctx.getNameParser("");
  -
  -      try
  -      {
  -         if( cacheJndiName != null )
  -            cachePolicy = (CachePolicy) ctx.lookup(cacheJndiName);
  -      }
  -      catch(NamingException e)
  -      {
  -      }
  -      theLog.info("startService, cachePolicy="+cachePolicy);
  -      // Bind the default SecurityProxyFactory instance under 
java:/SecurityProxyFactory
  -      SecurityProxyFactory proxyFactory = (SecurityProxyFactory) 
securityProxyFactoryClass.newInstance();
  -      ctx.bind("java:/SecurityProxyFactory", proxyFactory);
  -      theLog.info("startService, SecurityProxyFactory="+proxyFactory);
  -   }
  -
  -   protected void stopService()
  -   {
  -      InitialContext ic;
  -      try
  -      {
  -         ic = new InitialContext();
  -         ic.unbind("java:/jaas");
  -      }
  -      catch(CommunicationException e)
  -      {
  -         // Do nothing, the naming services is already stopped
  -      }
  -      catch(Exception e)
  -      {
  -         theLog.error("stopService", e);
  -      }
  -   }
  -   
  -   // ObjectFactory implementation ----------------------------------
  -   
  -   public static class SecurityDomainObjectFactory implements InvocationHandler, 
ObjectFactory
  -   {
  -      static Logger theLog = Logger.getLogger(SecurityDomainObjectFactory.class);
  -      /** Object factory implementation. This method returns a Context proxy
  -       that is only able to handle a lookup operation for an atomic name of
  -       a security domain.
  -      */
  -      public Object getObjectInstance(Object obj, Name name, Context nameCtx, 
Hashtable environment)
  -         throws Exception
  -      {
  -         ClassLoader loader = Thread.currentThread().getContextClassLoader();
  -         Class[] interfaces = {Context.class};
  -         Context ctx = (Context) Proxy.newProxyInstance(loader, interfaces, this);
  -         return ctx;
  -      }
  -
  -      private Object newSecurityMgr(String securityDomain) throws NamingException
  -      {
  -         Object securityMgr = null;
  -         try
  -         {   // Create instance of securityMgrClass
  -            Class[] parameterTypes = {String.class};
  -            Constructor ctor = securityMgrClass.getConstructor(parameterTypes);
  -            Object[] args = {securityDomain};
  -            securityMgr = ctor.newInstance(args);
  -            theLog.info("Created securityMgr="+securityMgr);
  -            // See if the security mgr supports an externalized cache policy
  -            try
  -            {
  -               parameterTypes[0] = CachePolicy.class;
  -               Method m = securityMgrClass.getMethod("setCachePolicy", 
parameterTypes);
  -               args[0] = cachePolicy;
  -               theLog.info("setCachePolicy, c="+args[0]);
  -               m.invoke(securityMgr, args);
  -            }
  -            catch(Exception e2)
  -            {   // No cache policy support, this is ok
  -            }
  -            theLog.info("Added "+securityDomain+", "+securityMgr+" to map");
  -            jsmMap.put(securityDomain, securityMgr);
  -         }
  -         catch(Exception e2)
  -         {
  -            theLog.error("Failed to create sec mgr", e2);
  -            throw new NamingException("Failed to create sec mgr:"+e2.getMessage());
  -         }
  -         return securityMgr;
  -      }
  -
  -      public Object invoke(Object obj, Method method, Object[] args) throws 
Throwable
  -      {
  -         String methodName = method.getName();
  -         if( methodName.equals("toString") == true )
  -            return "java:/jaas Context proxy";
  -         if( methodName.equals("list") == true )
  -            return new DomainEnumeration(jsmMap.keys());
  -
  -         if( methodName.equals("lookup") == false )
  -            throw new OperationNotSupportedException("Only lookup is supported, 
op="+method);
  -         String securityDomain = null;
  -         Name name = null;
  -         if( args[0] instanceof String )
  -            name = parser.parse((String) args[0]);
  -         else
  -           name = (Name)args[0];
  -         securityDomain = name.get(0);
  -         Object binding = jsmMap.get(securityDomain);
  -         if( binding == null )
  -         {
  -            binding = newSecurityMgr(securityDomain);
  -            jsmMap.put(securityDomain, binding);
  -         }
  -         // Look for requests against the security manager
  -         if( name.size() == 2 )
  -         {
  -            String request = name.get(1);
  -            if( binding instanceof SubjectSecurityManager && 
request.equals("subject") )
  -            {
  -               SubjectSecurityManager ssm = (SubjectSecurityManager) binding;
  -               Subject subject = ssm.getActiveSubject();
  -               binding = subject;
  -            }
  -         }
  -         return binding;
  -      }
  -   }
  -   static class DomainEnumeration implements NamingEnumeration
  -   {
  -      Enumeration domains;
  -      DomainEnumeration(Enumeration domains)
  -      {
  -         this.domains = domains;
  -      }
  -
  -      public void close()
  -      {
  -      }
  -      public boolean hasMoreElements()
  -      {
  -         return domains.hasMoreElements();
  -      }
  -      public boolean hasMore()
  -      {
  -         return domains.hasMoreElements();
  -      }
  -      public Object next()
  -      {
  -         String name = (String) domains.nextElement();
  -         NameClassPair pair = new NameClassPair(name, securityMgrClassName);
  -         return pair;
  -      }
  -      public Object nextElement()
  -      {
  -         return domains.nextElement();
  -      }
  -   }
  -}
  +/*
  + * JBoss, the OpenSource EJB server
  + *
  + * Distributable under LGPL license.
  + * See terms of license at gnu.org.
  + */
  +
  +package org.jboss.security.plugins;
  +
  +import java.lang.reflect.Constructor;
  +import java.lang.reflect.InvocationHandler;
  +import java.lang.reflect.Method;
  +import java.lang.reflect.Proxy;
  +import java.util.Enumeration;
  +import java.util.Hashtable;
  +
  +import javax.naming.InitialContext;
  +import javax.naming.Context;
  +import javax.naming.Reference;
  +import javax.naming.RefAddr;
  +import javax.naming.StringRefAddr;
  +import javax.naming.Name;
  +import javax.naming.NameClassPair;
  +import javax.naming.NameParser;
  +import javax.naming.NamingEnumeration;
  +import javax.naming.NamingException;
  +import javax.naming.OperationNotSupportedException;
  +import javax.naming.spi.ObjectFactory;
  +import javax.naming.CommunicationException;
  +import javax.security.auth.Subject;
  +
  +import javax.management.MalformedObjectNameException;
  +import javax.management.MBeanServer;
  +import javax.management.ObjectName;
  +
  +import org.jboss.logging.Logger;
  +import org.jboss.security.SecurityAssociation;
  +import org.jboss.security.SecurityProxyFactory;
  +import org.jboss.security.SubjectSecurityManager;
  +import org.jboss.util.CachePolicy;
  +import org.jboss.system.ServiceMBeanSupport;
  +
  +/**
  + * This is a JMX service which manages JAAS based SecurityManagers.
  + * JAAS SecurityManagers are responsible for validating credentials
  + * associated with principals. The service defaults to the
  + * org.jboss.security.plugins.JaasSecurityManager implementation but
  + * this can be changed via the securityManagerClass property.
  + *
  + * @see JaasSecurityManager
  + * @see SubjectSecurityManager
  + * @author <a href="[EMAIL PROTECTED]">Oleg Nitz</a>
  + * @author <a href="[EMAIL PROTECTED]">Rickard Oberg</a>
  + * @author <a href="mailto:[EMAIL PROTECTED]";>Scott Stark</a>
  + */
  +public class JaasSecurityManagerService
  +extends ServiceMBeanSupport
  +implements JaasSecurityManagerServiceMBean
  +{
  +   /** The logging interface */
  +   private static Logger theLog;
  +
  +   /** The class that provides the security manager implementation */
  +   private static String securityMgrClassName = 
"org.jboss.security.plugins.JaasSecurityManager";
  +
  +   /** The loaded securityMgrClassName */
  +   private static Class securityMgrClass;
  +
  +   /** The security credential cache policy, shared by all security mgrs */
  +   private static CachePolicy cachePolicy;
  +   private static String cacheJndiName;
  +
  +   /** The class that provides the SecurityProxyFactory implementation */
  +   private static String securityProxyFactoryClassName = 
"org.jboss.security.SubjectSecurityProxyFactory";
  +   private static Class securityProxyFactoryClass = 
org.jboss.security.SubjectSecurityProxyFactory.class;
  +
  +   private static Hashtable jsmMap = new Hashtable();
  +   private static NameParser parser;
  +
  +   static
  +   {
  +      // use thread-local principal and credential propagation
  +      SecurityAssociation.setServer();
  +      // Get a theLog interface
  +      theLog = Logger.getLogger(JaasSecurityManagerService.class);
  +   }
  +
  +   /**
  +    * The constructor does nothing as the security manager is created
  +    * on each lookup into java:/jaas/xxx. This is also why all variables
  +    * in this class are static.
  +    */
  +   public JaasSecurityManagerService()
  +   {
  +   }
  +
  +   public String getSecurityManagerClassName()
  +   {
  +      return securityMgrClassName;
  +   }
  +
  +   public void setSecurityManagerClassName(String className)
  +   throws ClassNotFoundException
  +   {
  +      securityMgrClassName = className;
  +      ClassLoader loader = Thread.currentThread().getContextClassLoader();
  +      securityMgrClass = loader.loadClass(securityMgrClassName);
  +   }
  +
  +   public String getSecurityProxyFactoryClassName()
  +   {
  +      return securityProxyFactoryClassName;
  +   }
  +
  +   public void setSecurityProxyFactoryClassName(String className)
  +   throws ClassNotFoundException
  +   {
  +      securityProxyFactoryClassName = className;
  +      ClassLoader loader = Thread.currentThread().getContextClassLoader();
  +      securityProxyFactoryClass = loader.loadClass(securityProxyFactoryClassName);
  +   }
  +
  +   /** Get the jndi name under which the authentication cache policy is found */
  +   public String getAuthenticationCacheJndiName()
  +   {
  +      return cacheJndiName;
  +   }
  +
  +   /** Set the jndi name under which the authentication cache policy is found */
  +   public void setAuthenticationCacheJndiName(String jndiName)
  +   {
  +      this.cacheJndiName = jndiName;
  +   }
  +
  +   /**
  +    * flush the cache policy for the indicated security domain if the security 
manager
  +    * instance supports a flushCache() method.
  +    */
  +   public void flushAuthenticationCache(String securityDomain)
  +   {
  +      String jndiName = "java:/jaas/" + securityDomain;
  +      try
  +      {
  +         InitialContext ic = new InitialContext();
  +         Object securityMgr = ic.lookup(jndiName);
  +         Class clazz = securityMgr.getClass();
  +         Class[] argTypes = {};
  +         Method flush = clazz.getMethod("flushCache", argTypes);
  +         Object[] args = {};
  +         flush.invoke(securityMgr, args);
  +      }
  +      catch(Exception e)
  +      {
  +         theLog.error("flushAuthenticationCache failed", e);
  +      }
  +   }
  +
  +   public String getName()
  +   {
  +      return "JAAS Security Manager";
  +   }
  +
  +   protected ObjectName getObjectName(MBeanServer server, ObjectName name)
  +   throws MalformedObjectNameException
  +   {
  +      return new ObjectName(OBJECT_NAME);
  +   }
  +
  +   protected void startService() throws Exception
  +   {
  +      InitialContext ic = new InitialContext();
  +
  +      // Bind reference to SM subcontext in JNDI
  +      // Uses JNDI federation to handle the "java:/jaas" context ourselves
  +      RefAddr refAddr = new StringRefAddr("nns", "JSM");
  +      String factoryName = SecurityDomainObjectFactory.class.getName();
  +      Reference jsmsRef = new Reference("javax.naming.Context", refAddr, 
factoryName, null);
  +      Context ctx = new InitialContext();
  +      ctx.rebind("java:/jaas", jsmsRef);
  +      parser = ctx.getNameParser("");
  +
  +      try
  +      {
  +         if(cacheJndiName != null)
  +            cachePolicy = (CachePolicy)ctx.lookup(cacheJndiName);
  +      }
  +      catch(NamingException e)
  +      {
  +         theLog.warn("No cache policy object found under name '" + cacheJndiName + 
"'");
  +      }
  +      theLog.info("startService, cachePolicy=" + cachePolicy);
  +      // Bind the default SecurityProxyFactory instance under 
java:/SecurityProxyFactory
  +      SecurityProxyFactory proxyFactory = 
(SecurityProxyFactory)securityProxyFactoryClass.newInstance();
  +      ctx.bind("java:/SecurityProxyFactory", proxyFactory);
  +      theLog.info("startService, SecurityProxyFactory=" + proxyFactory);
  +   }
  +
  +   protected void stopService()
  +   {
  +      InitialContext ic;
  +      try
  +      {
  +         ic = new InitialContext();
  +         ic.unbind("java:/jaas");
  +      }
  +      catch(CommunicationException e)
  +      {
  +         // Do nothing, the naming services is already stopped
  +      }
  +      catch(Exception e)
  +      {
  +         theLog.error("stopService", e);
  +      }
  +   }
  +
  +   // ObjectFactory implementation ----------------------------------
  +
  +   public static class SecurityDomainObjectFactory implements InvocationHandler, 
ObjectFactory
  +   {
  +      static Logger theLog = Logger.getLogger(SecurityDomainObjectFactory.class);
  +
  +      /**
  +       * Object factory implementation. This method returns a Context proxy
  +       * that is only able to handle a lookup operation for an atomic name of
  +       * a security domain.
  +       */
  +      public Object getObjectInstance(Object obj, Name name, Context nameCtx, 
Hashtable environment)
  +      throws Exception
  +      {
  +         ClassLoader loader = Thread.currentThread().getContextClassLoader();
  +         Class[] interfaces = {Context.class};
  +         Context ctx = (Context)Proxy.newProxyInstance(loader, interfaces, this);
  +         return ctx;
  +      }
  +
  +      private Object newSecurityMgr(String securityDomain) throws NamingException
  +      {
  +         Object securityMgr = null;
  +         try
  +         { // Create instance of securityMgrClass
  +            Class[] parameterTypes = {String.class};
  +            Constructor ctor = securityMgrClass.getConstructor(parameterTypes);
  +            Object[] args = {securityDomain};
  +            securityMgr = ctor.newInstance(args);
  +            theLog.info("Created securityMgr=" + securityMgr);
  +            // See if the security mgr supports an externalized cache policy
  +            try
  +            {
  +               parameterTypes[0] = CachePolicy.class;
  +               Method m = securityMgrClass.getMethod("setCachePolicy", 
parameterTypes);
  +               args[0] = cachePolicy;
  +               theLog.info("setCachePolicy, c=" + args[0]);
  +               m.invoke(securityMgr, args);
  +            }
  +            catch(Exception e2)
  +            { // No cache policy support, this is ok
  +            }
  +            theLog.info("Added " + securityDomain + ", " + securityMgr + " to map");
  +            jsmMap.put(securityDomain, securityMgr);
  +         }
  +         catch(Exception e2)
  +         {
  +            theLog.error("Failed to create sec mgr", e2);
  +            throw new NamingException("Failed to create sec mgr:" + 
e2.getMessage());
  +         }
  +         return securityMgr;
  +      }
  +
  +      public Object invoke(Object obj, Method method, Object[] args) throws 
Throwable
  +      {
  +         String methodName = method.getName();
  +         if(methodName.equals("toString") == true)
  +            return "java:/jaas Context proxy";
  +         if(methodName.equals("list") == true)
  +            return new DomainEnumeration(jsmMap.keys());
  +
  +         if(methodName.equals("lookup") == false)
  +            throw new OperationNotSupportedException("Only lookup is supported, 
op=" + method);
  +         String securityDomain = null;
  +         Name name = null;
  +         if(args[0] instanceof String)
  +            name = parser.parse((String)args[0]);
  +         else
  +            name = (Name)args[0];
  +         securityDomain = name.get(0);
  +         Object binding = jsmMap.get(securityDomain);
  +         if(binding == null)
  +         {
  +            binding = newSecurityMgr(securityDomain);
  +            jsmMap.put(securityDomain, binding);
  +         }
  +         // Look for requests against the security manager
  +         if(name.size() == 2)
  +         {
  +            String request = name.get(1);
  +            if(binding instanceof SubjectSecurityManager && 
request.equals("subject"))
  +            {
  +               SubjectSecurityManager ssm = (SubjectSecurityManager)binding;
  +               Subject subject = ssm.getActiveSubject();
  +               binding = subject;
  +            }
  +         }
  +         return binding;
  +      }
  +   }
  +
  +
  +   static class DomainEnumeration implements NamingEnumeration
  +   {
  +      Enumeration domains;
  +
  +      DomainEnumeration(Enumeration domains)
  +      {
  +         this.domains = domains;
  +      }
  +
  +      public void close()
  +      {
  +      }
  +
  +      public boolean hasMoreElements()
  +      {
  +         return domains.hasMoreElements();
  +      }
  +
  +      public boolean hasMore()
  +      {
  +         return domains.hasMoreElements();
  +      }
  +
  +      public Object next()
  +      {
  +         String name = (String)domains.nextElement();
  +         NameClassPair pair = new NameClassPair(name, securityMgrClassName);
  +         return pair;
  +      }
  +
  +      public Object nextElement()
  +      {
  +         return domains.nextElement();
  +      }
  +   }
  +}
  
  
  

_______________________________________________
Jboss-development mailing list
[EMAIL PROTECTED]
https://lists.sourceforge.net/lists/listinfo/jboss-development

Reply via email to