users@glassfish.java.net

Supplying settings to a application client as properties-A working solution

From: <glassfish_at_javadesktop.org>
Date: Fri, 13 Jun 2008 07:38:14 PDT

All right,
  here's the deal. LDAP Context cannot effectively be used outside the Server, since LDAPCtx isn't serializable. Using the JNDI External Resource to access LDAP is meant to materialize a java class stored in an LDAP Server and can only return the named lookup resource. So, a AS outside of using it to verify a user 's credentials via Realms isn't much help when one has to fully access an LDAP Server from a client. So if one desires to access a LDAP Server through a application client one has to set up a direct connection to said server. The problem here is avoiding to hardwire the LDAP settings to the deploying jars. This problem is even more severe when one deploys the clients using java web start. There's also the issue of breaking the EJB specification if one tries to access I/O resources in a EJB.

I offer here a solution to this impediment. The approach is to allow an instance of a properties class to be passed to the application clients through an EJB. The properties include the necessary settings to setup a LDAP(Dir)Context. It has been successfully tested using SJSAS 9.1 (glassfish V2U2) on a windows XP machine and with a app client connecting from a remote location (the other pc in the room ;-) ). The usage workflow is:

A: define a EJB that will be used as the entry point for an app client. Setup proper security access for it (optionally using ProgrammaticLogin),
B: inject said EJB with a Resource (that I will define later) pointing to the properties,
C: materialize the ejb through a JNDI lookup in a static context in the application client's main class,
D: access the properties through a getter method.

Once the properties instance has been passed to the app client, one can setup a LDAPContext (or DirContext) to fully access an LDAP Server.

Server side setup:
   You will need to copy the SettingsContext library to the App Server's lib folder (e.g. C:\Sun\SDK\lib\SettingsContext.jar).

    Using a browser to access the admin console, go to Application Server/JVM Settngs/Path settigns and enter ${com.sun.aas.installRoot}/lib/SettingsContext.jar in Classpath Suffix (See image Server-Classpath.png). Press save. Restart the server.

   Access the admin console when the server has restarted. Go to Resources/JNDI/External Resources. Create a new External Resource with these parameters:

JNDI Name: MyApp/Settings

Resource Type: java.util.Properties

Factory Class: org.context.settingsContext.SettingsFactory

JNDI Lookup: c:/sun/sdk/domains/domain1/config/settings.properties

Of course, JNDI Name and JNDI lookup can be altered to whatever one desires. The settings.properties file can include anything deemed necessary to be passed to the app clients, not just LDAP settings. See image jndiExternalSettings.png. Press save. Restart the server.

The server side setup is complete.

On to the code.

Create a ejb. Here's the relevant code:

...
@Stateless
public class GrantAccessBean implements GrantAccessRemote {

@Resource(name="MyApp/Settings")
   private Properties settings;
  
   public Properties getSettings() {
      return settings;
   }
...

You can insert the resource related code into any EJB. But make sure that a proper security mechanism is set up.
Here is an example for sun-ejb-jar.xml:

      <ejb>
         <ejb-name>SecureAccessBean</ejb-name>
         <ior-security-config>
            <transport-config>
               <integrity>SUPPORTED</integrity>
               <confidentiality>SUPPORTED</confidentiality>
               <establish-trust-in-target>SUPPORTED</establish-trust-in-target>
               <establish-trust-in-client>SUPPORTED</establish-trust-in-client>
            </transport-config>
            <as-context>
               <auth-method>username_password</auth-method>
               <realm>LDAP-realm</realm>
               <required>true</required>
            </as-context>
            <sas-context>
               <caller-propagation>SUPPORTED</caller-propagation>
            </sas-context>
         </ior-security-config>
      </ejb>

On to the application client's declared main class.

Create a static Properties settings field:

   static Properties settings;

You can declare it final of course.
Inside a static context after the above declaration insert this code:

When using declarative security only (ServiceLocator is a convenience class to perform JNDI lookups (from Netbeans 6.1)) :

      SecureAccessRemote secureAccessRemote = null;
      try {
         secureAccessRemote = (SecureAccessRemote) ServiceLocatorRemote.getInstance().lookupJNDI("java:comp/env/SecureAccessBean");
          settings = secureAccessRemote.getSettings();
      } catch (Exception ex) {}
      finally {
         secureAccessRemote = null;
      }

Using the above way the application server's login dialog will pop up.

When using programmatic login (obtainCredentials is a simple extended swing dialog to supply username and password):

   boolean success;
   try {
      success = new ProgrammaticLogin().login(obtainCredentials.user, new String(obtainCredentials.pwd), "LDAP-realm", true);
   } catch (Exception exception) {
      success = false;
   }
   if (success) {
      SecureAccessRemote secureAccessRemote = null;
      try {
         secureAccessRemote = (SecureAccessRemote) ServiceLocatorRemote.getInstance().lookupJNDI("java:comp/env/SecureAccessBean");
          settings = secureAccessRemote.getSettings();
      } catch (Exception ex) {}
      finally {
         secureAccessRemote = null;
      }
   }
   ...

Programmatic login is very useful when one wants to retain access to the security credentials of a user (as is my case) .

That's all with the client code. The last step concerns the app client deployment descriptors.

Don't insert anything in application-client.xml. While everything will work fine, you will have an exception thrown at app client start up.

In sun-application-client.xml just insert:
  
   <resource-ref>
      <res-ref-name>MyApp/Settings</res-ref-name>
      <jndi-name>MyApp/Settings</jndi-name>
   </resource-ref>

That's it!!!

Included is the netbeans project of the library's source code and the library jar. No javadoc, no license, just a skeleton implementation of the context related interfaces.
I have done all I can to limit the security risk imposed by using this library. It would be nice if one picked this up and augmented it.

As a final note, you can change the public Object lookup(Name name) throws NamingException method in SettingsCtx so that you can have the method return any type of class instance you desire. You will have to change the Resource Type to the class type you define and alter the JNDI loookup to reflect this change. Trying to pass a java.io.File directly isn't a good idea, it brakes the specification and probably can't even work correctly.

Ideally the code can be altered to allow injecting the properties as a resource directly to the app client's main class but I simply can't do it. I suspect one has to define an orb/rmi/corba related initialcontext to allow serialization across the jvms but the solution escapes me. Any takers?

I hope this will be of use to some of you. Opinions, comments, objections and hopefully appreciations are welcome. :-)

Cheers,
Andreas


P.S To get the code, source and images access the web site of the mailing list and look for this message. The images are included in the source archive.
[Message sent by forum member 'mfg8876' (mfg8876)]

http://forums.java.net/jive/thread.jspa?messageID=280186