package com.coderising.litestruts; import java.beans.BeanInfo; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; public class Struts { public static View runAction(String actionName, Map<String, String> parameters) { /* * * 0. 读取配置文件struts.xml * * 1. 根据actionName找到相对应的class , 例如LoginAction, 通过反射实例化(创建对象) * 据parameters中的数据,调用对象的setter方法, 例如parameters中的数据是 ("name"="test" , * "password"="1234") , 那就应该调用 setName和setPassword方法 * * 2. 通过反射调用对象的exectue 方法, 并获得返回值,例如"success" * * 3. 通过反射找到对象的所有getter方法(例如 getMessage), 通过反射来调用, 把值和属性形成一个HashMap , 例如 * {"message": "登录成功"} , 放到View对象的parameters * * 4. 根据struts.xml中的 <result> 配置,以及execute的返回值, 确定哪一个jsp, * 放到View对象的jsp字段中。 * */ //0. Load Structs.xml and return a map Map<String,Action> actionMap = loadActionMap(); //1.Find and initialize action instance to actionName provided Action action = actionMap.get(actionName); Object instance = initializeActionInstance(action.getActionClass(),parameters); //2.invoke the execute method and get the result String result = executeAction(instance); //3.Extract info for view View view = new View(); view.setParameters(extractInfo(instance)); //4.set dispatcher jsp according to execution result view.setJsp(action.getActionResultJsp(result)); return view; } private static Map<String, Action> loadActionMap() { try { InputStream is = Struts.class.getResourceAsStream("struts.xml"); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document document = builder.parse(is); Element struct = document.getDocumentElement(); return getActions(struct); } catch (IOException e) { throw new RuntimeException("Error Reading Configuration XML",e); } catch (ParserConfigurationException e) { throw new RuntimeException("Error Parsing Configuration XML",e); } catch (SAXException e) { throw new RuntimeException("Error Parsing Configuration XML",e); } } /** * Parse the XML and construct ActionName:Action map * * @param Element struct, root of the struct xml * @return Map<String,Action> */ private static Map<String,Action> getActions(Element struct) { Map<String,Action> map = new HashMap(); NodeList nl = struct.getElementsByTagName(Action.CONST_ACTION); for (int i = 0; i < nl.getLength(); i++) { Node actionNode = nl.item(i); // Get action name and corresponding action class from property String actionClass = getAttribute(actionNode,Action.CONST_CLASS); String actionName = getAttribute(actionNode,Action.CONST_NAME); Action action = new Action(actionName, actionClass); // get results under action NodeList childNodes = actionNode.getChildNodes(); for (int j = 0; j < childNodes.getLength(); j++) { Node result = childNodes.item(j); //Only accept if Node Type is element and Node name is "result" if ((result.getNodeType() == result.ELEMENT_NODE) && (result.getNodeName() == Action.CONST_RESULT)) { String resultName = getAttribute(result,Action.CONST_NAME); String dispatcherJsp = result.getTextContent(); action.setActionResultJsp(resultName, dispatcherJsp); } } map.put(action.getActionName(), action); } System.out.println(map); return map; } /** * Get property from given node * @param node * @param key * @return attribute as String */ private static String getAttribute(Node node,String key){ NamedNodeMap map = node.getAttributes(); Node attriNode = map.getNamedItem(key); if(attriNode!=null && attriNode.getNodeType()==Node.ATTRIBUTE_NODE){ return attriNode.getNodeValue(); } return null; } /** * Initialize instance from given class name and parameters map * @param actionClass * @param parameters * @return instance of specified class */ private static Object initializeActionInstance(String actionClass,Map parameters){ try { Class clazz= Class.forName(actionClass); //Instantiate by calling constructor Constructor constructor = clazz.getConstructor(); constructor.setAccessible(true); Object instance = constructor.newInstance(new Object[]{}); //Check class propertes with instrospector BeanInfo beanInfo = Introspector.getBeanInfo(clazz); PropertyDescriptor[] props = beanInfo.getPropertyDescriptors(); for(PropertyDescriptor prop:props){ String propName = prop.getName(); Method propSetter = prop.getWriteMethod(); //If there is a setter for the property and also there is a value in parameter map //then invoke the setter method to set the values if(propSetter!=null && parameters.containsKey(propName)) { propSetter.invoke(instance, parameters.get(propName)); } } return instance; } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("Error initializing instance: ClassName="+actionClass,e); } } /** * Invoke the "execute" method from the action instance * @param Action instance * @return execute result as String */ private static String executeAction(Object instance){ Class clazz = instance.getClass(); try { //exepct no argument for execute method Method execute = clazz.getMethod("execute", new Class[0]); return (String)execute.invoke(instance, new Object[0]); } catch (Exception e) { throw new RuntimeException("Error executing action,class name="+clazz.getCanonicalName()); } } /** * Extracting Bean info by calling the getting method in the Action instance * @param instance * @return map */ private static Map extractInfo(Object instance){ Map<String,Object> map = new HashMap<String,Object>(); Class clazz = instance.getClass(); try{ Method[] methods = clazz.getMethods(); for(Method method:methods) { String methodName = method.getName(); if(methodName.startsWith("get")&&method.getParameterTypes().length==0) { Object methodReturn = method.invoke(instance, new Object[0]); //construct the properties name by getter method name,first character toLower case String propName = methodName.replaceFirst("get", ""); char[] propNameCharArr = propName.toCharArray(); propNameCharArr[0]=Character.toLowerCase(propNameCharArr[0]); map.put(String.valueOf(propNameCharArr), methodReturn); } } }catch(Exception e){ throw new RuntimeException("Error extracting info from Action Insance,class="+clazz.getCanonicalName(),e); } return map; } }