| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.sling.commons.fsclassloader.impl; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.Writer; |
| import java.util.LinkedHashMap; |
| import java.util.Map; |
| |
| import javax.servlet.Servlet; |
| import javax.servlet.ServletConfig; |
| import javax.servlet.ServletException; |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| |
| import org.apache.commons.io.IOUtils; |
| import org.apache.commons.lang3.StringEscapeUtils; |
| import org.apache.commons.lang3.StringUtils; |
| import org.apache.felix.webconsole.AbstractWebConsolePlugin; |
| import org.apache.sling.commons.classloader.ClassLoaderWriter; |
| import org.osgi.service.component.ComponentContext; |
| import org.osgi.service.component.annotations.Activate; |
| import org.osgi.service.component.annotations.Component; |
| import org.osgi.service.component.annotations.Reference; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * Web Console for the FileSystem Class Loader. Allows users to download Java |
| * and Class files. |
| */ |
| @Component(service = Servlet.class, |
| configurationPid = FSClassLoaderProvider.SHARED_CONFIGURATION_PID, |
| property = { |
| "service.description=Web Console for the FileSystem Class Loader", |
| "service.vendor=The Apache Software Foundation", |
| "felix.webconsole.label=" + FSClassLoaderWebConsole.APP_ROOT, |
| "felix.webconsole.title=File System Class Loader", |
| "felix.webconsole.css=" + FSClassLoaderWebConsole.RES_LOC + "/prettify.css", |
| "felix.webconsole.category=Sling" |
| }) |
| public class FSClassLoaderWebConsole extends AbstractWebConsolePlugin { |
| |
| static final String APP_ROOT = "fsclassloader"; |
| |
| static final String RES_LOC = APP_ROOT + "/res/ui"; |
| static final String POST_PARAM_CLEAR_CLASSLOADER = "clear"; |
| |
| private static final Logger LOG = LoggerFactory.getLogger(FSClassLoaderWebConsole.class); |
| |
| @Reference(target = "(component.name=org.apache.sling.commons.fsclassloader.impl.FSClassLoaderProvider)") |
| private ClassLoaderWriter classLoaderWriter; |
| |
| /** |
| * The root under which the class files are under |
| */ |
| private File root; |
| |
| /** |
| * The serialization UID |
| */ |
| private static final long serialVersionUID = -5728679635644481848L; |
| |
| /** |
| * The servlet configuration |
| */ |
| private ServletConfig config; |
| |
| /** |
| * Activate this component. Create the root directory. |
| * |
| * @param componentContext |
| * the component context |
| */ |
| @Activate |
| protected void activate(final ComponentContext componentContext, final FSClassLoaderComponentConfig config) { |
| // get the file root |
| this.root = CacheLocationUtils.getRootDir(componentContext.getBundleContext(), config); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see javax.servlet.Servlet#destroy() |
| */ |
| @Override |
| public void destroy() { |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see javax.servlet.Servlet#service(javax.servlet.ServletRequest, |
| * javax.servlet.ServletResponse) |
| */ |
| @Override |
| protected void doGet(HttpServletRequest request, HttpServletResponse response) |
| throws ServletException, IOException { |
| String file = request.getParameter("download"); |
| File toDownload = new File(root + file); |
| if (!StringUtils.isEmpty(file)) { |
| if (isValid(toDownload)) { |
| InputStream is = null; |
| try { |
| is = new FileInputStream(toDownload); |
| response.setHeader("Content-disposition", "attachment; filename=" + toDownload.getName()); |
| IOUtils.copy(is, response.getOutputStream()); |
| } finally { |
| IOUtils.closeQuietly(is); |
| IOUtils.closeQuietly(response.getOutputStream()); |
| } |
| } else { |
| response.sendError(404, "File " + file + " not found"); |
| } |
| } else if (request.getRequestURI().endsWith(RES_LOC + "/prettify.css")) { |
| response.setContentType("text/css"); |
| IOUtils.copy(getClass().getClassLoader().getResourceAsStream("/res/ui/prettify.css"), |
| response.getOutputStream()); |
| } else if (request.getRequestURI().endsWith(RES_LOC + "/prettify.js")) { |
| response.setContentType("application/javascript"); |
| IOUtils.copy(getClass().getClassLoader().getResourceAsStream("/res/ui/prettify.js"), |
| response.getOutputStream()); |
| } else if (request.getRequestURI().endsWith(RES_LOC + "/fsclassloader.js")) { |
| response.setContentType("application/javascript"); |
| IOUtils.copy(getClass().getClassLoader().getResourceAsStream("/res/ui/fsclassloader.js"), |
| response.getOutputStream()); |
| } else { |
| super.doGet(request, response); |
| } |
| } |
| |
| @Override |
| protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { |
| String clear = req.getParameter(POST_PARAM_CLEAR_CLASSLOADER); |
| boolean shouldClear = Boolean.parseBoolean(clear); |
| if (shouldClear) { |
| if (classLoaderWriter != null) { |
| boolean result = classLoaderWriter.delete(""); |
| if (result) { |
| resp.getWriter().write("{ \"status\" : \"success\" }"); |
| resp.setStatus(HttpServletResponse.SC_OK); |
| } else { |
| resp.getWriter().write( |
| "{ \"status\" : \"failure\", \"message\" : \"unable to clear classloader; check server log\" }"); |
| resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); |
| } |
| } else { |
| LOG.error( |
| "Cannot get a reference to org.apache.sling.commons.fsclassloader.impl.FSClassLoaderProvider"); |
| resp.getWriter().write( |
| "{ \"status\" : \"failure\", \"message\" : \"unable to clear classloader; check server log\" }"); |
| resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); |
| } |
| } else { |
| resp.getWriter().write("{ \"status\" : \"failure\", \"message\" : \"invalid command\" }"); |
| resp.setStatus(HttpServletResponse.SC_BAD_REQUEST); |
| } |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.felix.webconsole.AbstractWebConsolePlugin#getLabel() |
| */ |
| @Override |
| public String getLabel() { |
| return "fsclassloader"; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see javax.servlet.Servlet#getServletConfig() |
| */ |
| @Override |
| public ServletConfig getServletConfig() { |
| return this.config; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see javax.servlet.Servlet#getServletInfo() |
| */ |
| @Override |
| public String getServletInfo() { |
| return ""; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.felix.webconsole.AbstractWebConsolePlugin#getTitle() |
| */ |
| @Override |
| public String getTitle() { |
| return "File System Class Loader"; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig) |
| */ |
| @Override |
| public void init(ServletConfig config) throws ServletException { |
| this.config = config; |
| } |
| |
| /** |
| * Checks whether the specified file is a file and is underneath the root |
| * directory. |
| * |
| * @param file |
| * the file to check |
| * @return false if not a file or not under the root directory, true |
| * otherwise |
| * @throws IOException |
| */ |
| private boolean isValid(File file) throws IOException { |
| if (file.isFile()) { |
| File parent = file.getCanonicalFile().getAbsoluteFile().getParentFile(); |
| while (parent != null) { |
| if (parent.getCanonicalPath().equals(root.getCanonicalPath())) { |
| return true; |
| } |
| parent = parent.getParentFile(); |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Reads all of the files under the current file. |
| * |
| * @param current |
| * the current file |
| * @param root |
| * the root file |
| * @param scripts |
| * the map of scripts |
| * @throws IOException |
| * an exception occurs reading the files |
| */ |
| protected static void readFiles(File current, File root, Map<String, ScriptFiles> scripts) throws IOException { |
| if (current.isDirectory()) { |
| File[] children = current.listFiles(); |
| if (children != null) { |
| for (File f : children) { |
| readFiles(f, root, scripts); |
| } |
| } |
| } else { |
| String script = ScriptFiles.getScript(root, current); |
| if (!scripts.containsKey(script) && current.getName().endsWith(".java")) { |
| scripts.put(script, new ScriptFiles(root, current)); |
| } |
| } |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see |
| * org.apache.felix.webconsole.AbstractWebConsolePlugin#renderContent(javax |
| * .servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) |
| */ |
| @Override |
| protected void renderContent(HttpServletRequest request, HttpServletResponse response) |
| throws ServletException, IOException { |
| Map<String, ScriptFiles> scripts = new LinkedHashMap<String, ScriptFiles>(); |
| readFiles(root, root, scripts); |
| |
| Writer w = response.getWriter(); |
| |
| w.write("<link rel=\"stylesheet\" type=\"text/css\" href=\"" + RES_LOC + "/prettify.css\"></link>"); |
| w.write("<script type=\"text/javascript\" src=\"" + RES_LOC + "/prettify.js\"></script>"); |
| w.write("<script type=\"text/javascript\" src=\"" + RES_LOC + "/fsclassloader.js\"></script>"); |
| w.write("<script>$(document).ready(prettyPrint);</script>"); |
| w.write("<style>.prettyprint ol.linenums > li { list-style-type: decimal; } pre.prettyprint { white-space: pre-wrap; }</style>"); |
| String file = request.getParameter("view"); |
| File toView = new File(root + file); |
| w.write("<div id=\"classes\">"); |
| if (!StringUtils.isEmpty(file)) { |
| if (isValid(toView)) { |
| |
| w.write("<p class=\"statline ui-state-highlight\">Viewing Script: " + root + file + "</p><br/><br/>"); |
| |
| ScriptFiles scriptFiles = new ScriptFiles(root, toView); |
| |
| w.write("<table class=\"nicetable ui-widget\">"); |
| w.write("<tr class=\"header ui-widget-header\">"); |
| w.write("<th>Script</th>"); |
| w.write("<th>Class</th>"); |
| w.write("<th>Deps</th>"); |
| w.write("<th>Java</th>"); |
| w.write("</tr>"); |
| w.write("<tr class=\"ui-state-default\">"); |
| w.write("<td>" + scriptFiles.getScript() + "</td>"); |
| w.write("<td>[<a href=\"?download=" + scriptFiles.getClassFile() |
| + "\" target=\"_blank\">download</a>]</td>"); |
| w.write("<td>[<a href=\"?download=" + scriptFiles.getDepsFile() |
| + "\" target=\"_blank\">download</a>]</td>"); |
| w.write("<td>[<a href=\"?download=" + scriptFiles.getJavaFile() |
| + "\" target=\"_blank\">download</a>]</td>"); |
| w.write("</tr>"); |
| w.write("</table><br/><br/>"); |
| InputStream is = null; |
| try { |
| is = new FileInputStream(toView); |
| String contents = IOUtils.toString(is, "UTF-8"); |
| w.write("<pre class=\"prettyprint linenums\">"); |
| w.write(StringEscapeUtils.escapeHtml4(contents)); |
| w.write("</pre>"); |
| } finally { |
| IOUtils.closeQuietly(is); |
| } |
| } else { |
| response.sendError(404, "File " + file + " not found"); |
| } |
| } else { |
| w.write("<p class=\"statline ui-state-highlight\">File System ClassLoader Root: " + root |
| + " <span style=\"float: right\"><button type='button' id='clear'>Clear Class Loader</button></span></p>"); |
| if (scripts.values().size() > 0) { |
| w.write("<table class=\"nicetable ui-widget fsclassloader-has-classes\">"); |
| } else { |
| w.write("<table class=\"nicetable ui-widget\">"); |
| } |
| w.write("<tr class=\"header ui-widget-header\">"); |
| w.write("<th>View</th>"); |
| w.write("<th>Script</th>"); |
| w.write("</tr>"); |
| int i = 0; |
| for (ScriptFiles scriptFiles : scripts.values()) { |
| w.write("<tr class=\"" + (i % 2 == 0 ? "even" : "odd") + " ui-state-default\">"); |
| w.write("<td>[<a href=\"?view=" + scriptFiles.getJavaFile() + "\">view</a>]</td>"); |
| w.write("<td>" + scriptFiles.getScript() + "</td>"); |
| w.write("</tr>"); |
| i++; |
| } |
| w.write("</table>"); |
| } |
| w.write("</div>"); |
| } |
| } |