View Javadoc

1   /*
2    * Copyright 2008 Simon Martinelli, Rebenweg 32, 3236 Gampelen, Switzerland
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.simject.remoting.server;
17  
18  import java.beans.XMLDecoder;
19  import java.beans.XMLEncoder;
20  import java.io.ByteArrayOutputStream;
21  import java.io.IOException;
22  import java.io.ObjectInputStream;
23  import java.io.ObjectOutputStream;
24  import java.lang.reflect.InvocationTargetException;
25  import java.lang.reflect.Method;
26  import java.util.StringTokenizer;
27  import java.util.logging.Level;
28  import java.util.logging.Logger;
29  
30  import javax.servlet.ServletException;
31  import javax.servlet.http.HttpServlet;
32  import javax.servlet.http.HttpServletRequest;
33  import javax.servlet.http.HttpServletResponse;
34  
35  import org.simject.SimFactory;
36  import org.simject.util.Protocol;
37  import org.simject.util.SimConstants;
38  
39  /**
40   * This servlet can be used as endpoint for remote access over HTTP to a
41   * resource
42   * 
43   * @author Simon Martinelli
44   */
45  @SuppressWarnings("serial")
46  public class SimServerServlet extends HttpServlet {
47  
48  	private final static Logger logger = Logger
49  			.getLogger(SimServerServlet.class.getName());
50  
51  	/**
52  	 * Context parameter that contains the configuration file location
53  	 */
54  	private final static String SIMJECT_CONFIG = "simjectConfig";
55  
56  	/**
57  	 * Reference to the SimFactory
58  	 */
59  	private SimFactory simFactory;
60  
61  	@Override
62  	public void init() throws ServletException {
63  		if (this.simFactory == null) {
64  			// Get the context parameter with the config file
65  			final String configFile = this.getServletContext()
66  					.getInitParameter(SIMJECT_CONFIG);
67  			// create a SimFactory based on the config file
68  			this.simFactory = new SimFactory(configFile);
69  		}
70  	}
71  
72  	@Override
73  	protected void doPost(final HttpServletRequest req,
74  			final HttpServletResponse resp) throws ServletException,
75  			IOException {
76  		this.invokeMethod(req, resp);
77  	}
78  
79  	/**
80  	 * Invokes a method based on the parameters passed from the client
81  	 * 
82  	 * @param req
83  	 * @param resp
84  	 * @param obj
85  	 * @param args
86  	 * @throws IOException
87  	 * @throws IllegalAccessException
88  	 * @throws InvocationTargetException
89  	 * @throws IOException
90  	 * @throws ClassNotFoundException
91  	 * @throws NoSuchMethodException
92  	 * @throws SecurityException
93  	 */
94  	private void invokeMethod(final HttpServletRequest req,
95  			final HttpServletResponse resp) throws IOException {
96  
97  		Object result = null;
98  		try {
99  			// get the classname
100 			final String className = this.getClassName(req);
101 			// create a Class instance
102 			final Class<?> clazz = Class.forName(className);
103 			// and get the resource from the SimFactory
104 			final Object obj = this.simFactory.getResource(clazz);
105 			// get passed arguments and invoke the method
106 			final Object[] args = this.getArguments(req);
107 			// get the method name from the HTTP header
108 			final String methodString = req
109 					.getHeader(SimConstants.PARAM_METHOD);
110 
111 			// get the parameter types from the HTTP header
112 			final String paramTypesString = req
113 					.getHeader(SimConstants.PARAM_TYPES);
114 
115 			logger.info("Request for class <" + clazz.getName() + "> method <"
116 					+ methodString + "> params <" + paramTypesString + ">");
117 
118 			if (paramTypesString == null) {
119 				// method without parameters to invoke
120 				final Method method = obj.getClass().getMethod(methodString);
121 				result = method.invoke(obj);
122 			} else {
123 				// method with parameters should be called. Converts the string
124 				// from the header to Class array
125 				final Class<?>[] parameterTypes = this
126 						.getParameterTypes(paramTypesString);
127 				final Method method = obj.getClass().getMethod(methodString,
128 						parameterTypes);
129 
130 				result = method.invoke(obj, args);
131 			}
132 		} catch (Exception e) {
133 			// If an exception occurs during invocation put it in the result to
134 			// have it serialized
135 			logger.log(Level.INFO, "Exception occured during invocation", e);
136 			result = e;
137 		}
138 		if (result != null) {
139 			if (req.getContentType().equals(Protocol.Xml.getContentType())) {
140 				this.sendXmlResponse(resp, result);
141 			} else {
142 				this.sendBinaryResponse(resp, result);
143 			}
144 		}
145 	}
146 
147 	/**
148 	 * Sends the result as binary stream
149 	 * 
150 	 * @param resp
151 	 * @param result
152 	 * @throws IOException
153 	 */
154 	private void sendBinaryResponse(final HttpServletResponse resp,
155 			final Object result) throws IOException {
156 		final ByteArrayOutputStream baos = new ByteArrayOutputStream();
157 		final ObjectOutputStream oos = new ObjectOutputStream(baos);
158 		oos.writeObject(result);
159 		oos.close();
160 
161 		resp.setContentType(Protocol.Binary.getContentType());
162 		resp.getOutputStream().write(baos.toByteArray());
163 	}
164 
165 	/**
166 	 * Sends the result as XML stream
167 	 * 
168 	 * @param resp
169 	 * @param result
170 	 * @throws IOException
171 	 */
172 	private void sendXmlResponse(final HttpServletResponse resp,
173 			final Object result) throws IOException {
174 		resp.setContentType(Protocol.Xml.getContentType());
175 		final XMLEncoder encoder = new XMLEncoder(resp.getOutputStream());
176 		encoder.writeObject(result);
177 	}
178 
179 	/**
180 	 * Returns an array of Class containing the types of the parameters
181 	 * 
182 	 * @param parameterString
183 	 * @return
184 	 * @throws ClassNotFoundException
185 	 */
186 	private Class<?>[] getParameterTypes(final String parameterString)
187 			throws ClassNotFoundException {
188 		Class<?>[] parameters = new Class[0];
189 		if (parameterString != null) {
190 			// The parameter types are seperated by ","
191 			final StringTokenizer stokenizer = new StringTokenizer(
192 					parameterString, SimConstants.PARAM_TYPE_DELIM);
193 			parameters = new Class[stokenizer.countTokens()];
194 			int index = 0;
195 			while (stokenizer.hasMoreTokens()) {
196 				final String className = stokenizer.nextToken();
197 
198 				final Class<?> clazz = Class.forName(className);
199 				parameters[index] = clazz;
200 				index++;
201 			}
202 		}
203 		return parameters;
204 	}
205 
206 	/**
207 	 * Get Arguments
208 	 * 
209 	 * @param req
210 	 * @return
211 	 * @throws IOException
212 	 * @throws ClassNotFoundException
213 	 */
214 	private Object[] getArguments(final HttpServletRequest req)
215 			throws IOException, ClassNotFoundException {
216 
217 		Object args = null;
218 		if (req.getContentType().equals(Protocol.Xml.getContentType())) {
219 			final XMLDecoder decoder = new XMLDecoder(req.getInputStream());
220 			args = decoder.readObject();
221 		} else {
222 			final ObjectInputStream ois = new ObjectInputStream(req
223 					.getInputStream());
224 			args = ois.readObject();
225 		}
226 
227 		return (Object[]) args;
228 	}
229 
230 	/**
231 	 * Parses the class name out of the path info
232 	 * 
233 	 * @param req
234 	 * @return
235 	 */
236 	private String getClassName(final HttpServletRequest req) {
237 		final StringTokenizer stokenzier = new StringTokenizer(req
238 				.getPathInfo(), "/");
239 		String[] tokens = new String[stokenzier.countTokens()];
240 		int index = 0;
241 		while (stokenzier.hasMoreTokens()) {
242 			tokens[index] = stokenzier.nextToken();
243 			index++;
244 		}
245 		return tokens[0];
246 	}
247 
248 	@Override
249 	protected void doGet(final HttpServletRequest req,
250 			final HttpServletResponse resp) throws ServletException,
251 			IOException {
252 		this.doPost(req, resp);
253 	}
254 }