Write Adaptive, Dynamic Code Using Reflection

Write Adaptive, Dynamic Code Using Reflection

evelopers aren’t issued crystal balls with their computers, which makes anticipating future changes to business rules or an application’s runtime environment challenging. Fortunately, what your code knows at compile time doesn’t need to limit your application’s ability to react dynamically to changes in the runtime environment.

Techniques for reading initialization or properties files are well known but many developers are unfamiliar with Java’s Reflection API. Reflection provides a means to significantly enhance your code’s ability to respond to the runtime environment; differentiating program behavior based upon the user or server or any other runtime variable. This article will explore how the LoginContext class of the Java Authentication and Authorization Services (JAAS) leverages the Reflection API to implement a responsive security services layer by loading login modules on demand rather than at application startup. (JAAS is part of the core JDK as of Java 1.4 and is available as a separate download if you’re using Java 1.3x).

The Meaning of Reflection
The Reflection API is located in the java.lang.reflect package. If you take a quick look at the Javadoc for the package you’ll immediately notice that a number of the classes bear the names of fundamental Java language elements: Constructor, Field, and Method. This might give you the first hint of what the Reflection API is all about?providing a “reflection” of the inner workings of a class. Now, if all that we could do with this reflected view of things is get a report of a class’ elements that would be interesting but not especially useful for writing dynamic code. Fortunately, not only can we query a class about its constituent parts but we can also dynamically load and instantiate a class at runtime and invoke methods on objects of that class as well.

What You Need
Java 1.3 or later. (1.3 has reached its “end of life” according to Sun so I recommend downloading at least 1.4x if you can.)

One of the design challenges of using Reflection is that you need to clarify what services you wish to provide at runtime that you can’t (or don’t want to) implement statically. The provision of reusable services that vary based upon the runtime environment is one of the reasons that frameworks such as Struts and JAAS are created. However, even small applications can face design issues that make a dynamic solution preferable over a static one. A word of caution though, over-reliance on Reflection can lead to performance issues because of the extra overhead of determining what class(es) need to be loaded.

Author’s Note: This is a good time to break out a copy of the javax.security.auth.login.LoginContext source code, which you can find as part of Sun’s Java source code zip file download or download it as a separate, standalone file here.

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 LoginModule8.		    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.

Share the Post:
XDR solutions

The Benefits of Using XDR Solutions

Cybercriminals constantly adapt their strategies, developing newer, more powerful, and intelligent ways to attack your network. Since security professionals must innovate as well, more conventional endpoint detection solutions have evolved

AI is revolutionizing fraud detection

How AI is Revolutionizing Fraud Detection

Artificial intelligence – commonly known as AI – means a form of technology with multiple uses. As a result, it has become extremely valuable to a number of businesses across

AI innovation

Companies Leading AI Innovation in 2023

Artificial intelligence (AI) has been transforming industries and revolutionizing business operations. AI’s potential to enhance efficiency and productivity has become crucial to many businesses. As we move into 2023, several

data fivetran pricing

Fivetran Pricing Explained

One of the biggest trends of the 21st century is the massive surge in analytics. Analytics is the process of utilizing data to drive future decision-making. With so much of

kubernetes logging

Kubernetes Logging: What You Need to Know

Kubernetes from Google is one of the most popular open-source and free container management solutions made to make managing and deploying applications easier. It has a solid architecture that makes

ransomware cyber attack

Why Is Ransomware Such a Major Threat?

One of the most significant cyber threats faced by modern organizations is a ransomware attack. Ransomware attacks have grown in both sophistication and frequency over the past few years, forcing

data dictionary

Tools You Need to Make a Data Dictionary

Data dictionaries are crucial for organizations of all sizes that deal with large amounts of data. they are centralized repositories of all the data in organizations, including metadata such as