View Javadoc

1   /*
2    * Copyright (c) 2006-2007 Creative Sphere Limited.
3    * All rights reserved. This program and the accompanying materials
4    * are made available under the terms of the Eclipse Public License v1.0
5    * which accompanies this distribution, and is available at
6    * http://www.eclipse.org/legal/epl-v10.html
7    *
8    * Contributors:
9    *
10   *   Creative Sphere - initial API and implementation
11   *
12   */
13  package org.abstracthorizon.danube.beanconsole;
14  
15  import org.abstracthorizon.danube.connection.Connection;
16  import org.abstracthorizon.danube.http.HTTPConnection;
17  import org.abstracthorizon.danube.mvc.Controller;
18  import org.abstracthorizon.danube.mvc.ModelAndView;
19  
20  import java.beans.MethodDescriptor;
21  import java.beans.PropertyEditor;
22  import java.beans.PropertyEditorManager;
23  import java.io.UnsupportedEncodingException;
24  import java.lang.reflect.Method;
25  import java.net.URLDecoder;
26  import java.util.ArrayList;
27  import java.util.Collection;
28  import java.util.HashMap;
29  import java.util.Map;
30  
31  import org.springframework.context.ConfigurableApplicationContext;
32  
33  /**
34   * Invokes an arbitrary method on selected bean. The bean is defined with all but last part of the resource path.
35   *
36   * @author Daniel Sendula
37   */
38  public class InvokeController implements Controller {
39  
40      protected DisplayController displayController;
41  
42      /**
43       * Constructor
44       */
45      public InvokeController() {
46      }
47  
48      /**
49       * Constructor
50       * @param displayController reference to display controller
51       */
52      public InvokeController(DisplayController displayController) {
53          this.displayController = displayController;
54      }
55  
56      /** Method that handles a request. It returns following entries in model's map:
57       * <ul>
58       *   <li>connection - httpConnection passed into this controller</li>
59       *   <li>methodName - </li>
60       *   <li>name - name of the bean (last element of the path or Root Bean)</li>
61       *   <li>type - string representation of the type of the selected object</li>
62       *   <li>path - full path to the bean. Path elements are separated with &quot;.&quot; and if
63       *              element has dot in it then it is enclosed in &quot;&lt;&quot; and &quot;&gt;&quot; simbols</li>
64       *   <li>resourcePath - full path to the bean but always starting with &quot;/&quot; and never ending
65       *              with it. If root then empty string. It is used for creating URIs.</li>
66       *   <li>returnUri - URI this page is called from. It always ends with &quot;/&quot;</li>
67       *   <li>topUri and backUri - uris to previous bean and top - root bean. </li>
68       *   <li>current - reference to the object referenced with the path</li>
69       *   <li>propertiesError - if error occured setting properties</li>
70       *   <li>beans - list of {@link BeanDef} objects - if object is of {@link ConfigurableApplicationContext} type.</li>
71       *   <li>map - list of {@link BeanDef} objects - if object is of {@link Map} type.</li>
72       *   <li>collection - list of {@link BeanDef} objects - if object is of {@link Collection} type.</li>
73       *   <li>properties - list of {@link BeanDef} objects - object accessible properties.</li>
74       *   <li>methods - list of {@link MethodDescriptor} object - methods that are not used for properties' access.</li>
75       * </ul>
76       * <p>
77       *   For the way navigation is done check {@link BeanHelper#navigate(Object, String)} method documentation.
78       * </p>
79       */
80      public ModelAndView handleRequest(Connection connection) {
81          HTTPConnection httpConnection = (HTTPConnection)connection.adapt(HTTPConnection.class);
82  
83          Map<String, Object> model = new HashMap<String, Object>();
84  
85          model.put("connection", connection);
86  
87          String resourcePath = BeanHelper.createResourcePath(httpConnection.getComponentResourcePath());
88  
89          Object current = displayController.getRootObject();
90  
91          String methodName;
92          if (resourcePath.length() == 0) {
93              methodName = null;
94          } else {
95              int i = resourcePath.lastIndexOf('/');
96              if (i > 0) {
97                  methodName = resourcePath.substring(i+1);
98                  resourcePath = resourcePath.substring(0, i);
99              } else {
100                 methodName = resourcePath.substring(1);
101                 resourcePath = "";
102             }
103         }
104 
105         if (resourcePath.length() > 0) {
106             current = BeanHelper.navigate(current, resourcePath);
107         }
108         model.put("name", BeanHelper.createName(resourcePath));
109         model.put("path", BeanHelper.createPath(resourcePath));
110         model.put("type", BeanHelper.toTypeString(current));
111         model.put("resourcePath", resourcePath);
112         model.put("current", current);
113         model.put("methodName", BeanHelper.createName(resourcePath));
114 
115         execute(model, current, httpConnection, methodName);
116 
117         model.put("returnUri", httpConnection.getContextPath() + displayController.getComponentPath() + resourcePath);
118 
119         String accept = httpConnection.getRequestHeaders().getOnly("Accept");
120         String view = null;
121         if (accept != null) {
122             if (accept.indexOf("text/html") >= 0) {
123                 view = "html/result";
124             } else if (accept.indexOf("text/xml") >= 0) {
125                 view = "xml/result";
126             }
127         }
128         if (view == null) {
129             view = "text/result";
130         }
131 
132         ModelAndView modelAndView = new ModelAndView(view, model);
133         return modelAndView;
134     }
135 
136     /**
137      * Invokes the method
138      * @param model model map
139      * @param current current bean
140      * @param httpConnection http connection (for paramters)
141      * @param methodName method name
142      */
143     protected void execute(Map<String, Object> model, Object current, HTTPConnection httpConnection, String methodName) {
144         if (methodName == null) {
145             model.put("error", "Method name not specified.");
146             return;
147         }
148 
149         int paramNumber = 0;
150         while (httpConnection.getRequestParameters().getOnly(Integer.toString(paramNumber)) != null) {
151             paramNumber = paramNumber + 1;
152         }
153 
154         Method[] methods = current.getClass().getMethods();
155         ArrayList<Method> foundMethods = new ArrayList<Method>();
156         for (Method method : methods) {
157             if (method.getName().equals(methodName) && (method.getParameterTypes().length == paramNumber)) {
158                 foundMethods.add(method);
159             }
160         }
161         if (foundMethods.size() == 0) {
162             model.put("error", "Couldn't find method '" + methodName +"' that has " + paramNumber +" parameters.");
163             return;
164         }
165 
166         Method method = null;
167         if (foundMethods.size() > 1) {
168             model.put("error", "Found more then one method '" + methodName +"' that has " + paramNumber +" parameters.\nNot supported at the moment");
169             return;
170         }
171 
172         method = foundMethods.get(0);
173 
174 
175         Class<?>[] types = method.getParameterTypes();
176         Object[] obj = new Object[types.length];
177         for (int j = 0; j < types.length; j++) {
178             String value = (String)httpConnection.getRequestParameters().getOnly(Integer.toString(j));
179             if (value != null) {
180                 try {
181                     value = URLDecoder.decode(value, "UTF-8");
182                 } catch (UnsupportedEncodingException ignore) {
183                 }
184             }
185 
186             PropertyEditor propertyEditor = PropertyEditorManager.findEditor(types[j]);
187             if (propertyEditor != null) {
188                 propertyEditor.setAsText(value);
189                 obj[j] = propertyEditor.getValue();
190             } else {
191                 obj[j] = value;
192             }
193         }
194         try {
195             Object res = method.invoke(current, obj);
196             StringBuffer result = new StringBuffer();
197             result.append(res);
198 
199             model.put("result", result.toString());
200         } catch (Throwable t) {
201             model.put("error", BeanHelper.stackTrace(t));
202         }
203     }
204 
205     /**
206      * Returns reference to DisplayController instance
207      * @return reference to DisplayController instance
208      */
209     public DisplayController getDisplayController() {
210         return displayController;
211     }
212 
213     /**
214      * Sets reference to DisplayController instance
215      * @param displayController reference to DisplayController instance
216      */
217     public void setDisplayController(DisplayController displayController) {
218         this.displayController = displayController;
219     }
220 
221 }