Browse DevX
Sign up for e-mail newsletters from DevX


Write Adaptive, Dynamic Code Using Reflection : Page 2

Java's Reflection API allows you to write code that adapts to runtime conditions. And you don't have to be working in a large-scale application to reap its benefits, either. Find out how reflection can allow your applications to be more robust and flexible.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

A Security Layer Built with JAAS and Reflection
In a security context, the unknowns at runtime typically have to do with the user's identity and access privileges. We need to figure out who the user is, based upon the credentials provided and then determine what activities the user is authorized to perform. JAAS helps solve these problems by providing a security services layer that supports pluggable login modules. JAAS itself has no notion about what custom modules you write and so must reflectively construct the modules at runtime.

The login modules could handle user input from any kind of input device: a login page, a smart card reader, fingerprint reader, etc. JAAS abstracts the authentication and authorization layer away from the application so you can handle security in whatever way best suits your application. The only help you give JAAS is to specify the names of the login modules in a configuration file and, of course, you have to provide the compiled classes somewhere on the classpath that your application uses. At runtime, when a user attempts to authenticate, the requisite login modules are dynamically loaded and instantiated and their appropriate methods called.

If you use an editor that supports line numbering, such as Eclipse, the method that you're going to focus on is the private method invoke(String methodName) that starts at line 619 when looking at the complete Java source as provided by Sun. As I mentioned earlier, JAAS is going to iterate through a list of modules as specified in its configuration file.

1. for (int i = 0; i < moduleStack.length; i++) { 2. try { 3. int mIndex = 0; 4. Method[ ] methods = null; 5. if (moduleStack[i].module != null) { 6. methods = moduleStack[i].module.getClass().getMethods(); 7. } else { // instantiate the LoginModule 8. Class c = Class.forName (moduleStack[i].entry.getLoginModuleName(), true, contextClassLoader);

At line 4 an array of type Method is declared and then initialized at line 6. These methods stored in the array will be reflectively invoked further on. At line 8 the class represented by the name of the current moduleStack entry is located and loaded as a generic Class object. The second boolean parameter indicates that the class object should be initialized (note that initialization isn't instantiation: static variables and blocks are initialized and no object is created yet). The third argument is the java.lang.Classloader object that should be used to load the class. (See the resources section, in the left column, for more information on the intricacies of class loading.)

The snippet below demonstrates how a Constructor object is instantiated. The PARAMS variable (line 1) defined at the top of the LoginContext class and the args variable declared at line 3 both serve the same purpose. The array of objects will be used as the arguments for the creation of the object—at line 2 it is a Constructor object and at line 4 it is the actual instance of a LoginModule. In both of these instances the array of arguments is empty but obviously this isn't always the case. In other scenarios, you would match the constructor arguments to the arguments in the array, just as you would match a constructor's signature in a non-reflective scenario.

1. private static final Class[ ] PARAMS = { }; //Defined at the start of the class 2. Constructor constructor = c.getConstructor(PARAMS); 3. Object[] args = { }; // allow any object to be a LoginModule // as long as it conforms to the interface 4. moduleStack[i].module = constructor.newInstance(args); 5. methods = moduleStack[i].module.getClass().getMethods();

Seeing arguments as an array of type Class or Object may seem strange at first. Using such "generic" means is necessary, however, because we don't actually know what class is being instantiated at runtime. A Class or Object type can effectively be used to represent an argument of any type. Even primitive types can be represented in this manner using Boolean.TYPE, Integer.TYPE, etc. At line 4 the Constructor object that was created at line 2 is used to instantiate an instance of the LoginModule, and line 5 populates the class's methods into an array of type Method.

Next the module will be initialized. The INIT_METHOD variable is a static variable that holds the actual method name that is to be executed so when it is found in the array of Methods at line 2 (by calling the method's getName() method) the code breaks out of the loop to initialize the Object array of arguments (line 3) and then invokes the method at line 4.

// call the LoginModule's initialize method 1. for (mIndex = 0; mIndex < methods.length; mIndex++) { 2. if (methods[mIndex].getName().equals(INIT_METHOD)) break; } 3. Object[] initArgs = {subject, callbackHandler, state, moduleStack[i].entry.getOptions() }; // invoke the LoginModule initialize method 4. methods[mIndex].invoke(moduleStack[i].module, initArgs);

Remember at this point the executing code could be invoking the initialization of any LoginModule class specified in the JAAS configuration. This is the power of reflection in a nutshell—your code isn't aware of the specific class or object being acted upon, rather the runtime environment determines it.

If you return to the full source code listing you'll see that the invoke() method resides in a try-catch block. Reflection assumes that the classes and their objects that we build and dynamically invoke methods upon actually exist. If it turns out that there isn't any such class or method—or that we don't have the proper security access to perform the requested action—then an appropriate exception will be thrown.

The use of reflection is commonplace in robust services frameworks such as JAAS and Struts, however, you don't have to be coding a large-scale application or framework to reap the benefits of reflection. Any time you have a variety of classes or interfaces and you can't be sure of what services your application might be called upon to provide at runtime Reflection is worth investigating.

Doug Tillman is a veteran Java and Python developer turned Scrum Master.
Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



Thanks for your registration, follow us on our social networks to keep up-to-date