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.lang.String;
24  import java.util.HashMap;
25  import java.util.HashSet;
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;
60      private CompilingListener jspListener; 
61  
62      private Map<String, HttpServlet> servletsByClassname = new HashMap<String, HttpServlet>();
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<String> newClasses;
74              private Map<String, HttpServlet> newServletsByClassname;
75  
76              public void onStart() {
77                  super.onStart();
78  
79                  newClasses = new HashSet<String>();
80                  newServletsByClassname = new HashMap<String, HttpServlet>(servletsByClassname);
81              }
82  
83              public void onStop() {
84                  super.onStop();
85  
86                  boolean reload = false;
87                  for (String clazzName : newClasses) {
88                      try {
89                          final Class clazz = classloader.loadClass(clazzName);
90  
91                          if (!HttpServlet.class.isAssignableFrom(clazz)) {
92                              log(clazzName + " is not a servlet");
93                              continue;
94                          }
95  
96                          // create new instance of jsp page
97                          final HttpServlet servlet = (HttpServlet) clazz.newInstance();
98                          newServletsByClassname.put(clazzName, servlet);
99  
100                         reload = true;
101                     } catch(Exception e) {
102                         log("", e);
103                     }
104                 }
105 
106                 if (reload) {
107                     log("Activating new map of servlets "+ newServletsByClassname);
108                     servletsByClassname = newServletsByClassname;
109                 }
110             }
111 
112             public void write(String pResourceName, byte[] pResourceData) {
113                 super.write(pResourceName, pResourceData);
114 
115                 if (pResourceName.endsWith(".class")) {
116 
117                     // compiler writes a new class, remember the classes to reload
118                     newClasses.add(pResourceName.replace('/', '.').substring(0, pResourceName.length() - ".class".length()));
119                 }
120             }
121 
122         };
123 
124         // listener that generates the java code from the jsp page and provides that to the compiler
125         jspListener = new CompilingListener(new JavaCompilerFactory().createCompiler("eclipse"), store) {
126 
127             private final JspGenerator transformer = new JspGenerator();
128             private final Map<String, byte[]> sources = new HashMap<String, byte[]>();
129             private final Set<String> resourceToCompile = new HashSet<String>();
130 
131             public void onStart(FilesystemAlterationObserver pObserver) {
132                 super.onStart(pObserver);
133 
134                 resourceToCompile.clear();
135             }
136 
137 
138             public void onFileChange(File pFile) {
139                 if (pFile.getName().endsWith(".jsp")) {
140                     final String resourceName = ConversionUtils.stripExtension(getSourceNameFromFile(observer, pFile)) + ".java";
141 
142                     log("Updating " + resourceName);
143 
144                     sources.put(resourceName, transformer.generateJavaSource(resourceName, pFile));
145 
146                     resourceToCompile.add(resourceName);
147                 }
148                 super.onFileChange(pFile);
149             }
150 
151 
152             public void onFileCreate(File pFile) {
153                 if (pFile.getName().endsWith(".jsp")) {
154                     final String resourceName = ConversionUtils.stripExtension(getSourceNameFromFile(observer, pFile)) + ".java";
155 
156                     log("Creating " + resourceName);
157 
158                     sources.put(resourceName, transformer.generateJavaSource(resourceName, pFile));
159 
160                     resourceToCompile.add(resourceName);
161                 }
162                 super.onFileCreate(pFile);
163             }
164 
165 
166             public String[] getResourcesToCompile(FilesystemAlterationObserver pObserver) {
167                 // we only want to compile the jsp pages
168                 final String[] resourceNames = new String[resourceToCompile.size()];
169                 resourceToCompile.toArray(resourceNames);
170                 return resourceNames;
171             }
172 
173 
174             public ResourceReader getReader( final FilesystemAlterationObserver pObserver ) {
175                 return new JspReader(sources, super.getReader(pObserver));
176             }
177         };
178         jspListener.addReloadNotificationListener(classloader);
179         
180         fam = new FilesystemAlterationMonitor();
181         fam.addListener(serverpagesDir, jspListener);
182         fam.start();
183     }
184 
185     private String convertRequestToServletClassname( final HttpServletRequest request ) {
186 
187         String path = request.getPathInfo().substring(1);
188 
189         return ConversionUtils.stripExtension(path).replace('/', '.');
190     }
191 
192     protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
193 
194         log("Request " + request.getRequestURI());
195 
196         final CompilationResult result = jspListener.getCompilationResult();
197         final CompilationProblem[] errors = result.getErrors();
198 
199         if (errors.length > 0) {
200 
201             // if there are errors we provide the compilation errors instead of the jsp page
202 
203             final PrintWriter out = response.getWriter();
204 
205             out.append("<html><body>");
206 
207             for (CompilationProblem problem : errors) {
208                 out.append(problem.toString()).append("<br/>").append('\n');
209             }
210 
211             out.append("</body></html>");
212 
213             out.flush();
214             out.close();
215             return;
216         }
217 
218         final String servletClassname = convertRequestToServletClassname(request);
219 
220         log("Checking for serverpage " + servletClassname);
221 
222         final HttpServlet servlet = servletsByClassname.get(servletClassname);
223 
224         if (servlet == null) {
225             log("No servlet  for " + request.getRequestURI());
226             response.sendError(404);
227             return;
228         }
229 
230         log("Delegating request to " + servletClassname);
231 
232         servlet.service(request, response);
233     }
234 
235     public void destroy() {
236 
237         fam.stop();
238 
239         super.destroy();
240     }
241 }