Author: radu Date: Fri Jun 17 16:16:54 2016 New Revision: 1748876 URL: http://svn.apache.org/viewvc?rev=1748876&view=rev Log: SLING-5601 - The File System Classloader Console Plugin should allow wiping the classloader's cache
* added support for clearing the classloader through the web console plug-in Added: sling/trunk/bundles/commons/fsclassloader/src/main/resources/res/ui/fsclassloader.js sling/trunk/bundles/commons/fsclassloader/src/test/ sling/trunk/bundles/commons/fsclassloader/src/test/java/ sling/trunk/bundles/commons/fsclassloader/src/test/java/org/ sling/trunk/bundles/commons/fsclassloader/src/test/java/org/apache/ sling/trunk/bundles/commons/fsclassloader/src/test/java/org/apache/sling/ sling/trunk/bundles/commons/fsclassloader/src/test/java/org/apache/sling/commons/ sling/trunk/bundles/commons/fsclassloader/src/test/java/org/apache/sling/commons/fsclassloader/ sling/trunk/bundles/commons/fsclassloader/src/test/java/org/apache/sling/commons/fsclassloader/impl/ sling/trunk/bundles/commons/fsclassloader/src/test/java/org/apache/sling/commons/fsclassloader/impl/FSClassLoaderWebConsoleTest.java Modified: sling/trunk/bundles/commons/fsclassloader/pom.xml sling/trunk/bundles/commons/fsclassloader/src/main/java/org/apache/sling/commons/fsclassloader/impl/FSClassLoaderWebConsole.java Modified: sling/trunk/bundles/commons/fsclassloader/pom.xml URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/fsclassloader/pom.xml?rev=1748876&r1=1748875&r2=1748876&view=diff ============================================================================== --- sling/trunk/bundles/commons/fsclassloader/pom.xml (original) +++ sling/trunk/bundles/commons/fsclassloader/pom.xml Fri Jun 17 16:16:54 2016 @@ -120,11 +120,35 @@ <version>2.5</version> <scope>provided</scope> </dependency> - <dependency> - <groupId>org.apache.felix</groupId> - <artifactId>org.apache.felix.webconsole</artifactId> - <version>3.0.0</version> - <scope>provided</scope> - </dependency> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.webconsole</artifactId> + <version>3.0.0</version> + <scope>provided</scope> + </dependency> + + <!-- Testing --> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <version>1.10.19</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.powermock</groupId> + <artifactId>powermock-api-mockito</artifactId> + <version>1.6.5</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> </dependencies> </project> Modified: sling/trunk/bundles/commons/fsclassloader/src/main/java/org/apache/sling/commons/fsclassloader/impl/FSClassLoaderWebConsole.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/fsclassloader/src/main/java/org/apache/sling/commons/fsclassloader/impl/FSClassLoaderWebConsole.java?rev=1748876&r1=1748875&r2=1748876&view=diff ============================================================================== --- sling/trunk/bundles/commons/fsclassloader/src/main/java/org/apache/sling/commons/fsclassloader/impl/FSClassLoaderWebConsole.java (original) +++ sling/trunk/bundles/commons/fsclassloader/src/main/java/org/apache/sling/commons/fsclassloader/impl/FSClassLoaderWebConsole.java Fri Jun 17 16:16:54 2016 @@ -39,9 +39,13 @@ import org.apache.felix.scr.annotations. import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Properties; import org.apache.felix.scr.annotations.Property; +import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.Service; import org.apache.felix.webconsole.AbstractWebConsolePlugin; +import org.apache.sling.commons.classloader.ClassLoaderWriter; import org.osgi.service.component.ComponentContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Web Console for the FileSystem Class Loader. Allows users to download Java @@ -62,6 +66,12 @@ public class FSClassLoaderWebConsole ext 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 = "(service.pid=org.apache.sling.commons.fsclassloader.impl.FSClassLoaderProvider)") + private ClassLoaderWriter classLoaderWriter; /** * Represents a set of class, java and deps files for a script. @@ -205,11 +215,42 @@ public class FSClassLoaderWebConsole ext IOUtils.copy( getClass().getClassLoader().getResourceAsStream( "/res/ui/prettify.js"), response.getOutputStream()); - } else { + } 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) * @@ -328,10 +369,13 @@ public class FSClassLoaderWebConsole ext + "/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)) { @@ -363,7 +407,7 @@ public class FSClassLoaderWebConsole ext InputStream is = null; try { is = new FileInputStream(toView); - String contents = IOUtils.toString(is); + String contents = IOUtils.toString(is, "UTF-8"); w.write("<pre class=\"prettyprint linenums\">"); w.write(StringEscapeUtils.escapeHtml4(contents)); w.write("</pre>"); @@ -374,11 +418,13 @@ public class FSClassLoaderWebConsole ext response.sendError(404, "File " + file + " not found"); } } else { - w.write("<p class=\"statline ui-state-highlight\">File System ClassLoader Root: " - + root + "</p>"); - - w.write("<table class=\"nicetable ui-widget\">"); + + 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>"); @@ -395,5 +441,6 @@ public class FSClassLoaderWebConsole ext } w.write("</table>"); } + w.write("</div>"); } } Added: sling/trunk/bundles/commons/fsclassloader/src/main/resources/res/ui/fsclassloader.js URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/fsclassloader/src/main/resources/res/ui/fsclassloader.js?rev=1748876&view=auto ============================================================================== --- sling/trunk/bundles/commons/fsclassloader/src/main/resources/res/ui/fsclassloader.js (added) +++ sling/trunk/bundles/commons/fsclassloader/src/main/resources/res/ui/fsclassloader.js Fri Jun 17 16:16:54 2016 @@ -0,0 +1,69 @@ +/******************************************************************************* + * 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. + ******************************************************************************/ +(function () { + 'use strict'; + + $(document).ready(function () { + toggleButton(hasClasses()); + + $('#clear').on('click', function (e) { + e.preventDefault(); + var classes = hasClasses(); + if (classes) { + clearCache(); + toggleButton(classes); + } + + }); + }); + + function clearCache() { + $.ajax({ + type: 'POST', + data: {'clear': true}, + dataType: 'json', + global: false + }).success( + function () { + window.location.reload(); + } + ).fail( + function (jqXHR) { + var response, message; + try { + response = JSON.parse(jqXHR.responseText); + message = response.message; + } catch (err) { + // do nothing + } + if (message) { + alert('Error: ' + message); + } else { + alert('An unknown error was encountered. Please check the server logs.'); + } + } + ); + } + + function hasClasses () { + return $('table.fsclassloader-has-classes').length > 0; + } + + function toggleButton(toggle) { + $('#clear').attr('disabled', !toggle); + } +})(); Added: sling/trunk/bundles/commons/fsclassloader/src/test/java/org/apache/sling/commons/fsclassloader/impl/FSClassLoaderWebConsoleTest.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/fsclassloader/src/test/java/org/apache/sling/commons/fsclassloader/impl/FSClassLoaderWebConsoleTest.java?rev=1748876&view=auto ============================================================================== --- sling/trunk/bundles/commons/fsclassloader/src/test/java/org/apache/sling/commons/fsclassloader/impl/FSClassLoaderWebConsoleTest.java (added) +++ sling/trunk/bundles/commons/fsclassloader/src/test/java/org/apache/sling/commons/fsclassloader/impl/FSClassLoaderWebConsoleTest.java Fri Jun 17 16:16:54 2016 @@ -0,0 +1,98 @@ +/******************************************************************************* + * 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.PrintWriter; +import java.io.StringWriter; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.sling.commons.classloader.ClassLoaderWriter; +import org.junit.After; +import org.junit.Test; +import org.mockito.Mockito; +import org.powermock.reflect.Whitebox; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.*; + +public class FSClassLoaderWebConsoleTest { + + private FSClassLoaderWebConsole console; + private ClassLoaderWriter classLoaderWriter; + + @After + public void after() { + console = null; + classLoaderWriter = null; + } + + @Test + public void testClearClassLoaderOK() throws Exception { + setFixture(true); + HttpServletRequest request = mock(HttpServletRequest.class); + HttpServletResponse response = mock(HttpServletResponse.class); + StringWriter stringWriter = new StringWriter(); + PrintWriter writer = new PrintWriter(stringWriter); + when(response.getWriter()).thenReturn(writer); + when(request.getParameter(FSClassLoaderWebConsole.POST_PARAM_CLEAR_CLASSLOADER)).thenReturn("true"); + console.doPost(request, response); + verify(classLoaderWriter).delete(""); + verify(response).setStatus(HttpServletResponse.SC_OK); + assertEquals("{ \"status\" : \"success\" }", stringWriter.toString()); + } + + @Test + public void testClearClassLoaderWrongCommand() throws Exception { + setFixture(true); + HttpServletRequest request = mock(HttpServletRequest.class); + HttpServletResponse response = mock(HttpServletResponse.class); + StringWriter stringWriter = new StringWriter(); + PrintWriter writer = new PrintWriter(stringWriter); + when(response.getWriter()).thenReturn(writer); + when(request.getParameter(FSClassLoaderWebConsole.POST_PARAM_CLEAR_CLASSLOADER)).thenReturn("random"); + console.doPost(request, response); + verify(classLoaderWriter, Mockito.times(0)).delete(""); + verify(response).setStatus(HttpServletResponse.SC_BAD_REQUEST); + assertEquals("{ \"status\" : \"failure\", \"message\" : \"invalid command\" }", stringWriter.toString()); + } + + @Test + public void testClearClassLoaderUnableToClean() throws Exception { + setFixture(false); + HttpServletRequest request = mock(HttpServletRequest.class); + HttpServletResponse response = mock(HttpServletResponse.class); + StringWriter stringWriter = new StringWriter(); + PrintWriter writer = new PrintWriter(stringWriter); + when(response.getWriter()).thenReturn(writer); + when(request.getParameter(FSClassLoaderWebConsole.POST_PARAM_CLEAR_CLASSLOADER)).thenReturn("true"); + console.doPost(request, response); + verify(classLoaderWriter).delete(""); + verify(response).setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + assertEquals("{ \"status\" : \"failure\", \"message\" : \"unable to clear classloader; check server log\" }", + stringWriter.toString()); + } + + private void setFixture(boolean clwReturn) { + console = spy(new FSClassLoaderWebConsole()); + classLoaderWriter = mock(ClassLoaderWriter.class); + when(classLoaderWriter.delete("")).thenReturn(clwReturn); + Whitebox.setInternalState(console, "classLoaderWriter", classLoaderWriter); + } + + +}