Home Java Servlets and Reflection

Servlets and Reflection

by admin

More recently, in the article Servlets – a little trick with Reflection , parsed a trick to get a url of the form :
If you go further, you can use Reflection to implement a construction of the following kind :
Servlets and Reflection
Where, we have several controller classes in the project, and each of them has its own actions. And most importantly, we can pass parameters from URI into controller methods. For example :


First let’s deal with the servlet. We have the same BaseServlet class, inherited from HttpServlet.

package projectManagment;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import projectManagment.controllers.Controller;public class BaseServlet extends HttpServlet {private ControllerFactory factory;public BaseServlet () {super();factory = new ControllerFactory();}@Overrideprotected void service(HttpServletRequest request, HttpServletResponse response) {try {String name = getControllerName(request);if (name == null)name = "index";Controller controller = factory.create(name);Controller.setHttp(request, response, getServletContext());String method = getMethodName(request);if (method == null)method = "index";callMethod(controller, method, request, response);} catch (Exception e) {System.out.println("Error: " + e.getMessage());e.printStackTrace();}}public String getControllerName (HttpServletRequest request) {String path = request.getRequestURI().substring(request.getContextPath().length());if (path == null || "".equals(path) || "/".equals(path))return null;String controller = path.substring(1, path.lastIndexOf("/"));if (controller != null !"".equals(controller))return controller;return null;}private String getMethodName (HttpServletRequest request) {String method = request.getRequestURI().substring(request.getContextPath().length());if (method != null !"".equals(method) !"/".equals(method))return method.substring(method.lastIndexOf("/") + 1, method.length() );return null;}private void callMethod (Controller controller, String methodName, HttpServletRequest request, HttpServletResponse response) throwsIllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {for (Method method : controller.getClass().getMethods()) {if (method.getName().equalsIgnoreCase(methodName)) {controller.getClass().getMethod(methodName, method.getParameterTypes()).invoke(controller);}}}}

The most interesting thing happens in the service method. First we get the controller name with the getControllerName helper method, which parses the URI. Then we use ControllerFactory, which creates an object of the controller class we want. The ControllerFactory code is very simple :

package projectManagment;import java.util.HashMap;import java.util.Map;import projectManagment.controllers.*;public class ControllerFactory {public ControllerFactory() {map = defaultMap();}public Controller create (String controllerName) {Class ControllerClass = (Class) map.get(controllerName);if (ControllerClass == null)throw new RuntimeException(getClass() + " was unable to find an controller named '" + controllerName + "'.");Controller controllerInstance = null;try {controllerInstance = (Controller) ControllerClass.newInstance();} catch (Exception e) {e.printStackTrace();}return controllerInstance;}protected Map<String, Class<?> > defaultMap() {Map<String, Class<?> > map = new HashMap<String, Class<?> > ();map.put("index", Index.class);return map;}protected Map<String, Class<?> > map;}

Now we just need to figure out how to call the method. First we get its name with getMethodName, and then we pass the necessary parameters to callMethod. This searches for the appropriate method in the controller and then calls it.
All the controllers we want to use must be prescribed in the defaultMap method of ControllerFactory.
Let’s move on to the basic controller class.

package projectManagment.controllers;import javax.servlet.ServletContext;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class Controller {static protected HttpServletResponse response;static protected HttpServletRequest request;static protected ServletContext context;public Controller () {request = null;response = null;context = null;}static public void setHttp (HttpServletRequest req, HttpServletResponse res, ServletContext con) {request = req;response = res;context = con;}}

The setHttp method allows us to teach our controller to talk. Here we can also add methods for authorization, redirect, etc.
Now let’s make a test controller called Index. It will have two methods index and show.

package projectManagment.controllers;public class Index extends Controller {public static void index () {System.out.println("Index method");}public static void show () {String value = request.getParameter("name");System.out.println("Hello " + value);}}

Don’t forget to configure the mapping in web.xml as follows :

<?xml version="1.0" encoding="UTF-8"?><web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"><servlet><servlet-name> Demo</servlet-name><servlet-class> projectManagment.BaseServlet</servlet-class></servlet><servlet-mapping><servlet-name> Demo</servlet-name><url-pattern> /</url-pattern></servlet-mapping></web-app>

Let’s test our servlet. At the request of /host/demo/ we will see the line “Index method" in the console, and at the request of /host/demo/index/show?name=Anton — “Hello Anton”.

Passing parameters to a method

To parse parameters from URI in methods, you have to use a construct of the form :

String strValue = request.getParametr("intParamName");Integer value = Intger.parseInt(strValue);

Performing such simple actions over and over again is tedious, and the beauty of the code suffers. The problem with parsing parameters can be solved by forwarding them as parameters to a method. To do this you need to use annotations.

package projectManagment.controllers;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;@Retention(RetentionPolicy.RUNTIME)public @interface Param {String name();}

The show method now looks like this :

public static void show (@Param(name = "name") String name, @Param(name = "age") Integer age) {System.out.println("Hello " + name + ", you are " + age + " years old");}

That’s not all, you need to pass parameters from URI to called method in callMethod.

private void callMethod(Controller controller, String methodName, HttpServletRequest request, HttpServletResponse response) throwsNoSuchMethodException, RuntimeException, SecurityException, IllegalAccessException, InvocationTargetException {for (Method method : controller.getClass().getMethods()) {if (method.getName().equalsIgnoreCase(methodName)) {Object[] params = new Object[method.getParameterTypes().length];Annotation[][] parameterAnnotations = method.getParameterAnnotations();Class[] parameterTypes = method.getParameterTypes();int i = 0;for(Annotation[] annotations : parameterAnnotations){Class parameterType = parameterTypes[i];for(Annotation annotation : annotations){if(annotation instanceof Param){Param myAnnotation = (Param) annotation;Object value = null;if (parameterType.getSimpleName().equals("Integer")) {try {value = Integer.parseInt(request.getParameter(myAnnotation.name()));} catch (NumberFormatException e) {value = null;}}else {value = request.getParameter(myAnnotation.name());}params[i++] = value;System.out.println("param: " + parameterType.getSimpleName());System.out.println("name : " + myAnnotation.name());System.out.println("value: " + value);}}}controller.getClass().getMethod(method.getName(), method.getParameterTypes()).invoke(controller, params);}}}

Now when you ask /host/demo/index/show?name=Antonage=20, the console outputs "Hello Anton, you are 20 years old".

You may also like