View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.jci.examples.serverpages;
19  
20  import java.io.File;
21  import java.io.IOException;
22  import java.io.PrintWriter;
23  import java.util.HashMap;
24  import java.util.HashSet;
25  import java.util.Iterator;
26  import java.util.Map;
27  import java.util.Set;
28  
29  import javax.servlet.ServletException;
30  import javax.servlet.http.HttpServlet;
31  import javax.servlet.http.HttpServletRequest;
32  import javax.servlet.http.HttpServletResponse;
33  
34  import org.apache.commons.jci.ReloadingClassLoader;
35  import org.apache.commons.jci.compilers.CompilationResult;
36  import org.apache.commons.jci.compilers.JavaCompilerFactory;
37  import org.apache.commons.jci.listeners.CompilingListener;
38  import org.apache.commons.jci.monitor.FilesystemAlterationMonitor;
39  import org.apache.commons.jci.monitor.FilesystemAlterationObserver;
40  import org.apache.commons.jci.problems.CompilationProblem;
41  import org.apache.commons.jci.readers.ResourceReader;
42  import org.apache.commons.jci.stores.MemoryResourceStore;
43  import org.apache.commons.jci.stores.TransactionalResourceStore;
44  import org.apache.commons.jci.utils.ConversionUtils;
45  
46  
47  /**
48   * A mini JSP servlet that monitors a certain directory and
49   * recompiles and then instantiates the JSP pages as soon as
50   * they have changed.
51   *
52   * @author tcurdt
53   */
54  public final class ServerPageServlet extends HttpServlet {
55  
56      private static final long serialVersionUID = 1L;
57  
58      private final ReloadingClassLoader classloader = new ReloadingClassLoader(ServerPageServlet.class.getClassLoader());
59      private FilesystemAlterationMonitor fam = null;
60      private CompilingListener jspListener = null; 
61  
62      private Map servletsByClassname = new HashMap();
63  
64      public void init() throws ServletException {
65          super.init();
66  
67          final File serverpagesDir = new File(getServletContext().getRealPath("/") + getInitParameter("serverpagesDir"));
68  
69          log("Monitoring serverpages in " + serverpagesDir);
70  
71          final TransactionalResourceStore store = new TransactionalResourceStore(new MemoryResourceStore()) {
72  
73              private Set newClasses;
74              private Map newServletsByClassname;
75  
76              public void onStart() {
77                  super.onStart();
78  
79                  newClasses = new HashSet();
80                  newServletsByClassname = new HashMap(servletsByClassname);
81              }
82  
83              public void onStop() {
84                  super.onStop();
85  
86                  boolean reload = false;
87                  for (Iterator it = newClasses.iterator(); it.hasNext();) {
88                      final String clazzName = (String) it.next();
89  
90                      try {
91                          final Class clazz = classloader.loadClass(clazzName);
92  
93                          if (!HttpServlet.class.isAssignableFrom(clazz)) {
94                              log(clazzName + " is not a servlet");
95                              continue;
96                          }
97  
98                          // create new instance of jsp page
99                          final HttpServlet servlet = (HttpServlet) clazz.newInstance();
100                         newServletsByClassname.put(clazzName, servlet);
101 
102                         reload = true;
103                     } catch(Exception e) {
104                         log("", e);
105                     }
106                 }
107 
108                 if (reload) {
109                     log("Activating new map of servlets "+ newServletsByClassname);
110                     servletsByClassname = newServletsByClassname;
111                 }
112             }
113 
114             public void write(String pResourceName, byte[] pResourceData) {
115                 super.write(pResourceName, pResourceData);
116 
117                 if (pResourceName.endsWith(".class")) {
118 
119                     // compiler writes a new class, remember the classes to reload
120                     newClasses.add(pResourceName.replace('/', '.').substring(0, pResourceName.length() - ".class".length()));
121                 }
122             }
123 
124         };
125 
126         // listener that generates the java code from the jsp page and provides that to the compiler
127         jspListener = new CompilingListener(new JavaCompilerFactory().createCompiler("eclipse"), store) {
128 
129             private final JspGenerator transformer = new JspGenerator();
130             private final Map sources = new HashMap();
131             private final Set resourceToCompile = new HashSet();
132 
133             public void onStart(FilesystemAlterationObserver pObserver) {
134                 super.onStart(pObserver);
135 
136                 resourceToCompile.clear();
137             }
138 
139 
140             public void onFileChange(File pFile) {
141                 if (pFile.getName().endsWith(".jsp")) {
142                     final String resourceName = ConversionUtils.stripExtension(getSourceNameFromFile(observer, pFile)) + ".java";
143 
144                     log("Updating " + resourceName);
145 
146                     sources.put(resourceName, transformer.generateJavaSource(resourceName, pFile));
147 
148                     resourceToCompile.add(resourceName);
149                 }
150                 super.onFileChange(pFile);
151             }
152 
153 
154             public void onFileCreate(File pFile) {
155                 if (pFile.getName().endsWith(".jsp")) {
156                     final String resourceName = ConversionUtils.stripExtension(getSourceNameFromFile(observer, pFile)) + ".java";
157 
158                     log("Creating " + resourceName);
159 
160                     sources.put(resourceName, transformer.generateJavaSource(resourceName, pFile));
161 
162                     resourceToCompile.add(resourceName);
163                 }
164                 super.onFileCreate(pFile);
165             }
166 
167 
168             public String[] getResourcesToCompile(FilesystemAlterationObserver pObserver) {
169                 // we only want to compile the jsp pages
170                 final String[] resourceNames = new String[resourceToCompile.size()];
171                 resourceToCompile.toArray(resourceNames);
172                 return resourceNames;
173             }
174 
175 
176             public ResourceReader getReader( final FilesystemAlterationObserver pObserver ) {
177                 return new JspReader(sources, super.getReader(pObserver));
178             }
179         };
180         jspListener.addReloadNotificationListener(classloader);
181         
182         fam = new FilesystemAlterationMonitor();
183         fam.addListener(serverpagesDir, jspListener);
184         fam.start();
185     }
186 
187     private String convertRequestToServletClassname( final HttpServletRequest request ) {
188 
189         final String path = request.getPathInfo().substring(1);
190 
191         final String clazz = ConversionUtils.stripExtension(path).replace('/', '.');
192 
193         return clazz;
194     }
195 
196     protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
197 
198         log("Request " + request.getRequestURI());
199 
200         final CompilationResult result = jspListener.getCompilationResult();
201         final CompilationProblem[] errors = result.getErrors();
202 
203         if (errors.length > 0) {
204 
205             // if there are errors we provide the compilation errors instead of the jsp page
206 
207             final PrintWriter out = response.getWriter();
208 
209             out.append("<html><body>");
210 
211             for (int i = 0; i < errors.length; i++) {
212                 final CompilationProblem problem = errors[i];
213                 out.append(problem.toString()).append("<br/>").append('\n');
214             }
215 
216             out.append("</body></html>");
217 
218             out.flush();
219             out.close();
220             return;
221         }
222 
223         final String servletClassname = convertRequestToServletClassname(request);
224 
225         log("Checking for serverpage " + servletClassname);
226 
227         final HttpServlet servlet = (HttpServlet) servletsByClassname.get(servletClassname);
228 
229         if (servlet == null) {
230             log("No servlet  for " + request.getRequestURI());
231             response.sendError(404);
232             return;
233         }
234 
235         log("Delegating request to " + servletClassname);
236 
237         servlet.service(request, response);
238     }
239 
240     public void destroy() {
241 
242         fam.stop();
243 
244         super.destroy();
245     }
246 }