KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jivesoftware > messenger > container > PluginServlet


1 /**
2  * $RCSfile: PluginServlet.java,v $
3  * $Revision: 1.25 $
4  * $Date: 2005/07/19 05:22:51 $
5  *
6  * Copyright (C) 2004 Jive Software. All rights reserved.
7  *
8  * This software is published under the terms of the GNU Public License (GPL),
9  * a copy of which is included in this distribution.
10  */

11
12 package org.jivesoftware.messenger.container;
13
14 import org.apache.jasper.JasperException;
15 import org.apache.jasper.JspC;
16 import org.dom4j.Document;
17 import org.dom4j.Element;
18 import org.dom4j.io.SAXReader;
19 import org.jivesoftware.util.JiveGlobals;
20 import org.jivesoftware.util.Log;
21 import org.jivesoftware.util.StringUtils;
22 import org.xml.sax.SAXException JavaDoc;
23
24 import javax.servlet.ServletConfig JavaDoc;
25 import javax.servlet.ServletException JavaDoc;
26 import javax.servlet.ServletOutputStream JavaDoc;
27 import javax.servlet.http.HttpServlet JavaDoc;
28 import javax.servlet.http.HttpServletRequest JavaDoc;
29 import javax.servlet.http.HttpServletResponse JavaDoc;
30 import java.io.*;
31 import java.util.HashMap JavaDoc;
32 import java.util.List JavaDoc;
33 import java.util.Map JavaDoc;
34 import java.util.concurrent.ConcurrentHashMap JavaDoc;
35
36 /**
37  * The plugin servlet acts as a proxy for web requests (in the admin console)
38  * to plugins. Since plugins can be dynamically loaded and live in a different place
39  * than normal Jive Messenger admin console files, it's not possible to have them
40  * added to the normal Jive Messenger admin console web app directory.<p>
41  *
42  * The servlet listens for requests in the form <tt>/plugins/[pluginName]/[JSP File]</tt>
43  * (e.g. <tt>/plugins/foo/example.jsp</tt>). It also listens for image requests in the
44  * the form <tt>/plugins/[pluginName]/images/*.png|gif</tt> (e.g.
45  * <tt>/plugins/foo/images/example.gif</tt>).<p>
46  *
47  * JSP files must be compiled and available via the plugin's class loader. The mapping
48  * between JSP name and servlet class files is defined in [pluginName]/web/web.xml.
49  * Typically, this file is auto-generated by the JSP compiler when packaging the plugin.
50  * Alternatively, if development mode is enabled for the plugin then the the JSP file
51  * will be dynamically compiled using JSPC.
52  *
53  * @author Matt Tucker
54  */

55 public class PluginServlet extends HttpServlet JavaDoc {
56
57     private static Map JavaDoc<String JavaDoc, HttpServlet JavaDoc> servlets;
58     private static PluginManager pluginManager;
59     private static ServletConfig JavaDoc servletConfig;
60
61     static {
62         servlets = new ConcurrentHashMap JavaDoc<String JavaDoc, HttpServlet JavaDoc>();
63     }
64
65     public void init(ServletConfig JavaDoc config) throws ServletException JavaDoc {
66         super.init(config);
67         servletConfig = config;
68     }
69
70     public void service(HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response)
71             throws ServletException JavaDoc, IOException
72     {
73         String JavaDoc pathInfo = request.getPathInfo();
74         if (pathInfo == null) {
75             response.setStatus(HttpServletResponse.SC_NOT_FOUND);
76             return;
77         }
78         else {
79             try {
80                 // Handle JSP requests.
81
if (pathInfo.endsWith(".jsp")) {
82                     if (handleDevJSP(pathInfo, request, response)) {
83                         return;
84                     }
85                     handleJSP(pathInfo, request, response);
86                     return;
87                 }
88                 // Handle image requests.
89
else if (pathInfo.endsWith(".gif") || pathInfo.endsWith(".png")) {
90                     handleImage(pathInfo, response);
91                     return;
92                 }
93                 // Handle servlet requests.
94
else if (getServlet(pathInfo) != null) {
95                     handleServlet(pathInfo, request, response);
96                 }
97                 // Anything else results in a 404.
98
else {
99                     response.setStatus(HttpServletResponse.SC_NOT_FOUND);
100                     return;
101                 }
102             }
103             catch (Exception JavaDoc e) {
104                 Log.error(e);
105                 response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
106                 return;
107             }
108         }
109     }
110
111     /**
112      * Registers all JSP page servlets for a plugin.
113      *
114      * @param manager the plugin manager.
115      * @param plugin the plugin.
116      * @param webXML the web.xml file containing JSP page names to servlet class file
117      * mappings.
118      */

119     public static void registerServlets(PluginManager manager, Plugin plugin, File webXML) {
120         pluginManager = manager;
121         if (!webXML.exists()) {
122             Log.error("Could not register plugin servlets, file " + webXML.getAbsolutePath() +
123                     " does not exist.");
124             return;
125         }
126         // Find the name of the plugin directory given that the webXML file
127
// lives in plugins/[pluginName]/web/web.xml
128
String JavaDoc pluginName = webXML.getParentFile().getParentFile().getParentFile().getName();
129         try {
130             // Make the reader non-validating so that it doesn't try to resolve external
131
// DTD's. Trying to resolve external DTD's can break on some firewall configurations.
132
SAXReader saxReader = new SAXReader(false);
133             try {
134                 saxReader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd",
135                         false);
136             }
137             catch (SAXException JavaDoc e) {
138                 Log.warn("Error setting SAXReader feature", e);
139             }
140             Document doc = saxReader.read(webXML);
141             // Find all <servlet> entries to discover name to class mapping.
142
List JavaDoc classes = doc.selectNodes("//servlet");
143             Map JavaDoc<String JavaDoc, Class JavaDoc> classMap = new HashMap JavaDoc<String JavaDoc, Class JavaDoc>();
144             for (int i = 0; i < classes.size(); i++) {
145                 Element servletElement = (Element)classes.get(i);
146                 String JavaDoc name = servletElement.element("servlet-name").getTextTrim();
147                 String JavaDoc className = servletElement.element("servlet-class").getTextTrim();
148                 classMap.put(name, manager.loadClass(plugin, className));
149             }
150             // Find all <servelt-mapping> entries to discover name to URL mapping.
151
List JavaDoc names = doc.selectNodes("//servlet-mapping");
152             for (int i = 0; i < names.size(); i++) {
153                 Element nameElement = (Element)names.get(i);
154                 String JavaDoc name = nameElement.element("servlet-name").getTextTrim();
155                 String JavaDoc url = nameElement.element("url-pattern").getTextTrim();
156                 // Register the servlet for the URL.
157
Class JavaDoc servletClass = classMap.get(name);
158                 Object JavaDoc instance = servletClass.newInstance();
159                 if (instance instanceof HttpServlet JavaDoc) {
160                     // Initialize the servlet then add it to the map..
161
((HttpServlet JavaDoc)instance).init(servletConfig);
162                     servlets.put(pluginName + url, (HttpServlet JavaDoc)instance);
163                 }
164                 else {
165                     Log.warn("Could not load " + (pluginName + url) + ": not a servlet.");
166                 }
167             }
168         }
169         catch (Throwable JavaDoc e) {
170             Log.error(e);
171         }
172     }
173
174     /**
175      * Unregisters all JSP page servlets for a plugin.
176      *
177      * @param webXML the web.xml file containing JSP page names to servlet class file
178      * mappings.
179      */

180     public static void unregisterServlets(File webXML) {
181         if (!webXML.exists()) {
182             Log.error("Could not unregister plugin servlets, file " + webXML.getAbsolutePath() +
183                     " does not exist.");
184             return;
185         }
186         // Find the name of the plugin directory given that the webXML file
187
// lives in plugins/[pluginName]/web/web.xml
188
String JavaDoc pluginName = webXML.getParentFile().getParentFile().getParentFile().getName();
189         try {
190             SAXReader saxReader = new SAXReader(false);
191             saxReader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd",
192                     false);
193             Document doc = saxReader.read(webXML);
194             // Find all <servelt-mapping> entries to discover name to URL mapping.
195
List JavaDoc names = doc.selectNodes("//servlet-mapping");
196             for (int i = 0; i < names.size(); i++) {
197                 Element nameElement = (Element)names.get(i);
198                 String JavaDoc url = nameElement.element("url-pattern").getTextTrim();
199                 // Destroy the servlet than remove from servlets map.
200
HttpServlet JavaDoc servlet = servlets.get(pluginName + url);
201                 servlet.destroy();
202                 servlets.remove(pluginName + url);
203                 servlet = null;
204             }
205         }
206         catch (Throwable JavaDoc e) {
207             Log.error(e);
208         }
209     }
210
211     /**
212      * Handles a request for a JSP page. It checks to see if a servlet is mapped
213      * for the JSP URL. If one is found, request handling is passed to it. If no
214      * servlet is found, a 404 error is returned.
215      *
216      * @param pathInfo the extra path info.
217      * @param request the request object.
218      * @param response the response object.
219      * @throws ServletException if a servlet exception occurs while handling the request.
220      * @throws IOException if an IOException occurs while handling the request.
221      */

222     private void handleJSP(String JavaDoc pathInfo, HttpServletRequest JavaDoc request,
223             HttpServletResponse JavaDoc response) throws ServletException JavaDoc, IOException
224     {
225         // Strip the starting "/" from the path to find the JSP URL.
226
String JavaDoc jspURL = pathInfo.substring(1);
227
228
229         HttpServlet JavaDoc servlet = servlets.get(jspURL);
230         if (servlet != null) {
231             servlet.service(request, response);
232             return;
233         }
234         else {
235             response.setStatus(HttpServletResponse.SC_NOT_FOUND);
236             return;
237         }
238     }
239
240     /**
241      * Handles a request for a Servlet. If one is found, request handling is passed to it.
242      * If no servlet is found, a 404 error is returned.
243      *
244      * @param pathInfo the extra path info.
245      * @param request the request object.
246      * @param response the response object.
247      * @throws ServletException if a servlet exception occurs while handling the request.
248      * @throws IOException if an IOException occurs while handling the request.
249      */

250     private void handleServlet(String JavaDoc pathInfo, HttpServletRequest JavaDoc request,
251                                HttpServletResponse JavaDoc response) throws ServletException JavaDoc, IOException {
252         // Strip the starting "/" from the path to find the JSP URL.
253
HttpServlet JavaDoc servlet = getServlet(pathInfo);
254         if (servlet != null) {
255             servlet.service(request, response);
256             return;
257         }
258         else {
259             response.setStatus(HttpServletResponse.SC_NOT_FOUND);
260             return;
261         }
262     }
263
264     /**
265      * Returns the correct servlet with mapping checks.
266      *
267      * @param pathInfo the pathinfo to map to the servlet.
268      * @return the mapped servlet, or null if no servlet was found.
269      */

270     private HttpServlet JavaDoc getServlet(String JavaDoc pathInfo) {
271         pathInfo = pathInfo.substring(1).toLowerCase();
272
273         HttpServlet JavaDoc servlet = servlets.get(pathInfo);
274         if (servlet == null) {
275             for (String JavaDoc key : servlets.keySet()) {
276                 int index = key.indexOf("/*");
277                 String JavaDoc searchkey = key;
278                 if (index != -1) {
279                     searchkey = key.substring(0, index);
280                 }
281                 if (searchkey.startsWith(pathInfo) || pathInfo.startsWith(searchkey)) {
282                     servlet = servlets.get(key);
283                     break;
284                 }
285             }
286         }
287         return servlet;
288     }
289
290     /**
291      * Handles a request for an image.
292      *
293      * @param pathInfo the extra path info.
294      * @param response the response object.
295      * @throws IOException if an IOException occurs while handling the request.
296      */

297     private void handleImage(String JavaDoc pathInfo, HttpServletResponse JavaDoc response) throws IOException {
298         String JavaDoc[] parts = pathInfo.split("/");
299         // Image request must be in correct format.
300
if (parts.length != 4) {
301             response.setStatus(HttpServletResponse.SC_NOT_FOUND);
302             return;
303         }
304         File pluginDirectory = new File(JiveGlobals.getHomeDirectory(), "plugins");
305         File image = new File(pluginDirectory, parts[1] + File.separator + "web" +
306                 File.separator + "images" + File.separator + parts[3]);
307         if (!image.exists()) {
308             response.setStatus(HttpServletResponse.SC_NOT_FOUND);
309             return;
310         }
311         else {
312             // Content type will be GIF or PNG.
313
String JavaDoc contentType = "image/gif";
314             if (pathInfo.endsWith(".png")) {
315                 contentType = "image/png";
316             }
317             response.setHeader("Content-disposition", "filename=\"" + image + "\";");
318             response.setContentType(contentType);
319             // Write out the image to the user.
320
InputStream in = null;
321             ServletOutputStream JavaDoc out = null;
322             try {
323                 in = new BufferedInputStream(new FileInputStream(image));
324                 out = response.getOutputStream();
325
326                 // Set the size of the file.
327
response.setContentLength((int)image.length());
328
329                 // Use a 1K buffer.
330
byte[] buf = new byte[1024];
331                 int len;
332                 while ((len = in.read(buf)) != -1) {
333                     out.write(buf, 0, len);
334                 }
335             }
336             finally {
337                 try {
338                     in.close();
339                 }
340                 catch (Exception JavaDoc ignored) {
341                 }
342                 try {
343                     out.close();
344                 }
345                 catch (Exception JavaDoc ignored) {
346                 }
347             }
348         }
349     }
350
351     /**
352      * Handles a request for a JSP page in development mode. If development mode is
353      * not enabled, this method returns false so that normal JSP handling can be performed.
354      * If development mode is enabled, this method tries to locate the JSP, compile
355      * it using JSPC, and then return the output.
356      *
357      * @param pathInfo the extra path info.
358      * @param request the request object.
359      * @param response the response object.
360      * @return true if this page request was handled; false if the request was not handled.
361      */

362     private boolean handleDevJSP(String JavaDoc pathInfo, HttpServletRequest JavaDoc request,
363             HttpServletResponse JavaDoc response)
364     {
365         String JavaDoc jspURL = pathInfo.substring(1);
366
367         // Handle pre-existing pages and fail over to pre-compiled pages.
368
int fileSeperator = jspURL.indexOf("/");
369         if (fileSeperator != -1) {
370             String JavaDoc pluginName = jspURL.substring(0, fileSeperator);
371             Plugin plugin = pluginManager.getPlugin(pluginName);
372
373             PluginDevEnvironment environment = pluginManager.getDevEnvironment(plugin);
374             // If development mode not turned on for plugin, return false.
375
if (environment == null) {
376                 return false;
377             }
378             File webDir = environment.getWebRoot();
379             if (webDir == null || !webDir.exists()) {
380                 return false;
381             }
382
383             File pluginDirectory = pluginManager.getPluginDirectory(plugin);
384
385             File compilationDir = new File(pluginDirectory, "classes");
386             compilationDir.mkdirs();
387
388             String JavaDoc jsp = jspURL.substring(fileSeperator + 1);
389
390             int indexOfLastSlash = jsp.lastIndexOf("/");
391             String JavaDoc relativeDir = "";
392             if (indexOfLastSlash != -1) {
393                 relativeDir = jsp.substring(0, indexOfLastSlash);
394                 relativeDir = relativeDir.replaceAll("//", ".") + '.';
395             }
396
397             File jspFile = new File(webDir, jsp);
398             String JavaDoc filename = jspFile.getName();
399             int indexOfPeriod = filename.indexOf(".");
400             if (indexOfPeriod != -1) {
401                 filename = "dev" + StringUtils.randomString(4);
402             }
403
404             JspC jspc = new JspC();
405             if (!jspFile.exists()) {
406                 return false;
407             }
408             try {
409                 jspc.setJspFiles(jspFile.getCanonicalPath());
410             }
411             catch (IOException e) {
412                 Log.error(e);
413             }
414             jspc.setOutputDir(compilationDir.getAbsolutePath());
415             jspc.setClassName(filename);
416             jspc.setCompile(true);
417
418             jspc.setClassPath(getClasspathForPlugin(plugin));
419             try {
420                 jspc.execute();
421
422                 try {
423                     Object JavaDoc servletInstance = pluginManager.loadClass(plugin, "org.apache.jsp." +
424                             relativeDir + filename).newInstance();
425                     HttpServlet JavaDoc servlet = (HttpServlet JavaDoc)servletInstance;
426                     servlet.init(servletConfig);
427                     servlet.service(request, response);
428                     return true;
429                 }
430                 catch (Exception JavaDoc e) {
431                     Log.error(e);
432                 }
433
434             }
435             catch (JasperException e) {
436                 Log.error(e);
437             }
438         }
439         return false;
440     }
441
442     /**
443      * Returns the classpath to use for the JSPC Compiler.
444      *
445      * @param plugin the plugin the jspc will handle.
446      * @return the classpath needed to compile a single jsp in a plugin.
447      */

448     private static String JavaDoc getClasspathForPlugin(Plugin plugin) {
449         StringBuilder JavaDoc builder = new StringBuilder JavaDoc();
450
451         File pluginDirectory = pluginManager.getPluginDirectory(plugin);
452
453         PluginDevEnvironment env = pluginManager.getDevEnvironment(plugin);
454
455         // Load all jars from lib
456
File libDirectory = new File(pluginDirectory, "lib");
457         File[] libs = libDirectory.listFiles();
458         for (int i = 0; i < libs.length; i++) {
459             File libFile = libs[i];
460             builder.append(libFile.getAbsolutePath() + ';');
461         }
462
463         File messengerRoot = pluginDirectory.getParentFile().getParentFile().getParentFile();
464         File messengerLib = new File(messengerRoot, "target//lib");
465
466         builder.append(messengerLib.getAbsolutePath() + "//servlet.jar;");
467         builder.append(messengerLib.getAbsolutePath() + "//messenger.jar;");
468         builder.append(messengerLib.getAbsolutePath() + "//jasper-compiler.jar;");
469         builder.append(messengerLib.getAbsolutePath() + "//jasper-runtime.jar;");
470         builder.append(env.getClassesDir().getAbsolutePath() + ";");
471
472         return builder.toString();
473     }
474 }
Popular Tags