1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.struts2;
22
23 import com.opensymphony.xwork2.util.URLUtil;
24 import com.opensymphony.xwork2.util.finder.ClassLoaderInterface;
25 import com.opensymphony.xwork2.util.finder.ClassLoaderInterfaceDelegate;
26 import com.opensymphony.xwork2.util.finder.UrlSet;
27 import com.opensymphony.xwork2.util.logging.Logger;
28 import com.opensymphony.xwork2.util.logging.LoggerFactory;
29 import org.apache.commons.io.FileUtils;
30 import org.apache.commons.lang.xwork.ObjectUtils;
31 import org.apache.commons.lang.xwork.StringUtils;
32 import org.apache.struts2.compiler.MemoryClassLoader;
33 import org.apache.struts2.compiler.MemoryJavaFileObject;
34 import org.apache.struts2.jasper.JasperException;
35 import org.apache.struts2.jasper.JspC;
36 import org.apache.struts2.jasper.compiler.JspUtil;
37
38 import javax.servlet.Servlet;
39 import javax.servlet.ServletContext;
40 import javax.servlet.ServletException;
41 import javax.servlet.jsp.JspPage;
42 import javax.servlet.jsp.HttpJspPage;
43 import javax.tools.*;
44 import java.io.File;
45 import java.io.IOException;
46 import java.net.URI;
47 import java.net.URISyntaxException;
48 import java.net.URL;
49 import java.security.CodeSource;
50 import java.security.ProtectionDomain;
51 import java.util.*;
52 import java.util.regex.Pattern;
53 import java.util.regex.Matcher;
54
55 /***
56 * Uses jasper to extract a JSP from the classpath to a file and compile it. The classpath used for
57 * compilation is built by finding all the jar files using the current class loader (Thread), plus
58 * directories.
59 */
60 public class JSPLoader {
61 private static final Logger LOG = LoggerFactory.getLogger(JSPLoader.class);
62
63 private static MemoryClassLoader classLoader = new MemoryClassLoader();
64 private static final String DEFAULT_PACKAGE = "org.apache.struts2.jsp";
65
66 private static final Pattern PACKAGE_PATTERN = Pattern.compile("package (.*?);");
67 private static final Pattern CLASS_PATTERN = Pattern.compile("public final class (.*?) ");
68
69 public Servlet load(String location) throws Exception {
70 location = StringUtils.substringBeforeLast(location, "?");
71
72 if (LOG.isDebugEnabled()) {
73 LOG.debug("Compiling JSP [#0]", location);
74 }
75
76
77 JspC jspC = compileJSP(location);
78 String source = jspC.getSourceCode();
79
80
81
82 String className = extractClassName(source);
83
84
85
86 compileJava(className, source, jspC.getTldAbsolutePaths());
87
88
89 Class clazz = Class.forName(className, false, classLoader);
90 return createServlet(clazz);
91 }
92
93 private String extractClassName(String source) {
94 Matcher matcher = PACKAGE_PATTERN.matcher(source);
95 matcher.find();
96 String packageName = matcher.group(1);
97
98 matcher = CLASS_PATTERN.matcher(source);
99 matcher.find();
100 String className = matcher.group(1);
101
102 return packageName + "." + className;
103 }
104
105 /***
106 * Creates and inits a servlet
107 */
108 private Servlet createServlet(Class clazz) throws IllegalAccessException, InstantiationException, ServletException {
109 JSPServletConfig config = new JSPServletConfig(ServletActionContext.getServletContext());
110
111 Servlet servlet = (Servlet) clazz.newInstance();
112 servlet.init(config);
113
114
115
116
117
118
119
120
121 return servlet;
122 }
123
124 /***
125 * Compiles the given source code into java bytecode
126 */
127 private void compileJava(String className, final String source, Set<String> extraClassPath) throws IOException {
128 if (LOG.isTraceEnabled())
129 LOG.trace("Compiling [#0], source: [#1]", className, source);
130
131 JavaCompiler compiler =
132 ToolProvider.getSystemJavaCompiler();
133
134 DiagnosticCollector<JavaFileObject> diagnostics =
135 new DiagnosticCollector<JavaFileObject>();
136
137
138 JavaFileManager jfm = new
139 ForwardingJavaFileManager<StandardJavaFileManager>(
140 compiler.getStandardFileManager(diagnostics, null, null)) {
141
142 @Override
143 public JavaFileObject getJavaFileForOutput(Location location,
144 String name,
145 JavaFileObject.Kind kind,
146 FileObject sibling) throws IOException {
147 MemoryJavaFileObject fileObject = new MemoryJavaFileObject(name, kind);
148 classLoader.addMemoryJavaFileObject(name, fileObject);
149 return fileObject;
150 }
151 };
152
153
154 String fileName = className.replace('.', '/') + ".java";
155 SimpleJavaFileObject sourceCodeObject = new SimpleJavaFileObject(toURI(fileName), JavaFileObject.Kind.SOURCE) {
156 @Override
157 public CharSequence getCharContent(boolean
158 ignoreEncodingErrors)
159 throws IOException, IllegalStateException,
160 UnsupportedOperationException {
161 return source;
162 }
163
164 };
165
166
167
168 List<String> optionList = new ArrayList<String>();
169 Set<String> classPath = new HashSet<String>();
170
171
172 ClassLoaderInterface classLoaderInterface = getClassLoaderInterface();
173 UrlSet urlSet = new UrlSet(classLoaderInterface);
174
175
176 List<URL> urls = urlSet.getUrls();
177
178 for (URL url : urls) {
179 URL normalizedUrl = URLUtil.normalizeToFileProtocol(url);
180 File file = FileUtils.toFile((URL) ObjectUtils.defaultIfNull(normalizedUrl, url));
181 if (file.exists())
182 classPath.add(file.getAbsolutePath());
183 }
184
185
186
187 classPath.add(getJarUrl(EmbeddedJSPResult.class));
188
189 classPath.add(getJarUrl(Servlet.class));
190
191 classPath.add(getJarUrl(JspPage.class));
192
193
194 for (Iterator<String> iterator = extraClassPath.iterator(); iterator.hasNext();) {
195 String entry = iterator.next();
196 classPath.add(entry);
197 }
198
199 String classPathString = StringUtils.join(classPath, File.pathSeparator);
200 if (LOG.isDebugEnabled()) {
201 LOG.debug("Compiling [#0] with classpath [#1]", className, classPathString);
202 }
203
204 optionList.addAll(Arrays.asList("-classpath", classPathString));
205
206
207 JavaCompiler.CompilationTask task = compiler.getTask(
208 null, jfm, diagnostics, optionList, null,
209 Arrays.asList(sourceCodeObject));
210
211 if (!task.call()) {
212 throw new StrutsException("Compilation failed:" + diagnostics.getDiagnostics().get(0).toString());
213 }
214 }
215
216 protected String getJarUrl(Class clazz) {
217 ProtectionDomain protectionDomain = clazz.getProtectionDomain();
218 CodeSource codeSource = protectionDomain.getCodeSource();
219 URL loc = codeSource.getLocation();
220 File file = FileUtils.toFile(loc);
221 return file.getAbsolutePath();
222 }
223
224 private JspC compileJSP(String location) throws JasperException {
225 JspC jspC = new JspC();
226 jspC.setClassLoaderInterface(getClassLoaderInterface());
227 jspC.setCompile(false);
228 jspC.setJspFiles(location);
229 jspC.setPackage(DEFAULT_PACKAGE);
230 jspC.execute();
231 return jspC;
232 }
233
234 private ClassLoaderInterface getClassLoaderInterface() {
235 ClassLoaderInterface classLoaderInterface = null;
236 ServletContext ctx = ServletActionContext.getServletContext();
237 if (ctx != null)
238 classLoaderInterface = (ClassLoaderInterface) ctx.getAttribute(ClassLoaderInterface.CLASS_LOADER_INTERFACE);
239
240 return (ClassLoaderInterface) ObjectUtils.defaultIfNull(classLoaderInterface, new ClassLoaderInterfaceDelegate(JSPLoader.class.getClassLoader()));
241 }
242
243 private static URI toURI(String name) {
244 try {
245 return new URI(name);
246 } catch (URISyntaxException e) {
247 throw new RuntimeException(e);
248 }
249 }
250 }