This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-log-webconsole.git
commit d8521ec1023e55d91a3f8f3b6fc1afea6cf20b69 Author: Chetan Mehrotra <[email protected]> AuthorDate: Wed Oct 12 17:02:00 2016 +0000 SLING-5932 - Move Web Console Plugin to separate bundle git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1764489 13f79535-47bb-0310-9956-ffa450edef68 --- pom.xml | 493 +++++++++++++++++++++ src/main/appended-resources/META-INF/LICENSE | 244 ++++++++++ src/main/appended-resources/META-INF/NOTICE | 12 + .../commons/log/webconsole/internal/Activator.java | 57 +++ .../webconsole/internal/LogWebConsolePlugin.java | 121 +++++ src/main/resources/res/ui/jquery.autocomplete.css | 38 ++ .../resources/res/ui/jquery.autocomplete.min.js | 9 + src/main/resources/res/ui/log.css | 22 + src/main/resources/res/ui/prettify.css | 1 + src/main/resources/res/ui/prettify.js | 30 ++ src/main/resources/res/ui/resourceresolver.css | 21 + src/main/resources/res/ui/slinglog.js | 167 +++++++ src/main/resources/res/ui/styles.css | 35 ++ .../log/webconsole/ITLogWebConsolePlugin.java | 56 +++ .../commons/log/webconsole/ITWebConsoleRemote.java | 146 ++++++ .../sling/commons/log/webconsole/LogTestBase.java | 165 +++++++ .../webconsole/remote/WebConsoleTestActivator.java | 131 ++++++ src/test/resources/exam.properties | 21 + src/test/resources/test-webconsole-remote.xml | 39 ++ 19 files changed, 1808 insertions(+) diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..94fbd53 --- /dev/null +++ b/pom.xml @@ -0,0 +1,493 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<!-- + 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. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.sling</groupId> + <artifactId>sling</artifactId> + <version>26</version> + <relativePath /> + </parent> + + <artifactId>org.apache.sling.commons.log.webconsole</artifactId> + <version>0.0.1-SNAPSHOT</version> + <packaging>bundle</packaging> + + <name>Apache Sling Log WebConsole</name> + <description> + This bundle provides webconsole plugin for Sling Commons Log + </description> + + <scm> + <connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/bundles/commons/log-webconsole</connection> + <developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/trunk/bundles/commons/log-webconsole</developerConnection> + <url>http://svn.apache.org/viewvc/sling/trunk/bundles/commons/log-webconsole</url> + </scm> + + <properties> + <slf4j.version>1.6.4</slf4j.version> + <logback.version>1.1.2</logback.version> + <pax-exam.version>3.5.0</pax-exam.version> + + <bundle.build.dir> + ${basedir}/target + </bundle.build.dir> + <bundle.file.name> + ${bundle.build.dir}/${project.build.finalName}.jar + </bundle.file.name> + </properties> + + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Bundle-Activator> + org.apache.sling.commons.log.webconsole.internal.Activator + </Bundle-Activator> + <Bundle-DocURL> + http://sling.apache.org/site/logging.html + </Bundle-DocURL> + <Embed-Dependency> + org.apache.sling.commons.osgi;inline= + org/apache/sling/commons/osgi/PropertiesUtil*.class + </Embed-Dependency> + <Import-Package> + javax.servlet; + javax.servlet.http;version=2.3, + * + </Import-Package> + </instructions> + </configuration> + </plugin> + <!-- Required for pax exam--> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <executions> + <execution> + <id>reserve-network-port</id> + <goals> + <goal>reserve-network-port</goal> + </goals> + <phase>pre-integration-test</phase> + <configuration> + <portNames> + <portName>http.port</portName> + </portNames> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>properties-maven-plugin</artifactId> + <version>1.0-alpha-2</version> + <executions> + <execution> + <goals> + <goal>set-system-properties</goal> + </goals> + <phase>pre-integration-test</phase> + <configuration> + <properties> + <property> + <name>project.bundle.file</name> + <value>${bundle.file.name}</value> + </property> + <property> + <name>http.port</name> + <value>${http.port}</value> + </property> + </properties> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.servicemix.tooling</groupId> + <artifactId>depends-maven-plugin</artifactId> + <version>1.2</version> + <executions> + <execution> + <id>generate-depends-file</id> + <goals> + <goal>generate-depends-file</goal> + </goals> + </execution> + </executions> + </plugin> + <!-- integration tests run with pax-exam --> + <plugin> + <artifactId>maven-failsafe-plugin</artifactId> + <executions> + <execution> + <goals> + <goal>integration-test</goal> + <goal>verify</goal> + </goals> + </execution> + </executions> + <configuration> + <systemPropertyVariables> + <project.bundle.file>${bundle.file.name}</project.bundle.file> + <project.boot.file>${bundle.file.name}</project.boot.file> + <coverage.command>${coverage.command}</coverage.command> + <http.port>${http.port}</http.port> + </systemPropertyVariables> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.rat</groupId> + <artifactId>apache-rat-plugin</artifactId> + <configuration> + <excludes> + <exclude>src/main/resources/res/ui/jquery.autocomplete.css</exclude> + <exclude>src/main/resources/res/ui/jquery.autocomplete.min.js</exclude> + <exclude>src/main/resources/res/ui/prettify.css</exclude> + <exclude>src/main/resources/res/ui/prettify.js</exclude> + <exclude>README.md</exclude> + </excludes> + </configuration> + </plugin> + </plugins> + </build> + + + + <reporting> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-javadoc-plugin</artifactId> + <configuration> + <!-- No javadocs at all --> + <skip>true</skip> + </configuration> + </plugin> + </plugins> + </reporting> + + <dependencies> + <dependency> + <groupId>biz.aQute.bnd</groupId> + <artifactId>bnd</artifactId> + <version>2.1.0</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + <version>${slf4j.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>jul-to-slf4j</artifactId> + <version>${slf4j.version}</version> + <scope>compile</scope> + <optional>true</optional> + </dependency> + <dependency> + <groupId>ch.qos.logback</groupId> + <artifactId>logback-classic</artifactId> + <version>${logback.version}</version> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>ch.qos.logback</groupId> + <artifactId>logback-core</artifactId> + <version>${logback.version}</version> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.commons.log</artifactId> + <version>4.0.7-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.commons.osgi</artifactId> + <version>2.2.0</version> + </dependency> + + <!-- OSGi Libraries not included here --> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.core</artifactId> + <version>5.0.0</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.compendium</artifactId> + <version>5.0.0</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.webconsole</artifactId> + <version>4.2.0</version> + </dependency> + + <!-- servlet API for the web console plugin --> + <dependency> + <groupId>javax.servlet</groupId> + <artifactId>servlet-api</artifactId> + <version>2.3</version> + </dependency> + + <!-- OSGi Command Line Shell support --> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.gogo.runtime</artifactId> + <version>0.6.1</version> + <scope>provided</scope> + </dependency> + + <!-- testing --> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + </dependency> + + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.framework</artifactId> + <version>4.4.1</version> + <scope>test</scope> + </dependency> + + <!-- Pax Exam Dependencies --> + <dependency> + <groupId>org.ops4j.pax.exam</groupId> + <artifactId>pax-exam-container-forked</artifactId> + <version>${pax-exam.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.ops4j.pax.exam</groupId> + <artifactId>pax-exam-junit4</artifactId> + <version>${pax-exam.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.ops4j.pax.exam</groupId> + <artifactId>pax-exam-link-mvn</artifactId> + <version>${pax-exam.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.ops4j.pax.tinybundles</groupId> + <artifactId>tinybundles</artifactId> + <version>2.0.0</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>javax.inject</groupId> + <artifactId>javax.inject</artifactId> + <version>1</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.configadmin</artifactId> + <version>1.6.0</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.http.bundle</artifactId> + <version>2.2.0</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> + <version>2.4</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>net.sourceforge.htmlunit</groupId> + <artifactId>htmlunit</artifactId> + <version>2.12</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.commons.logservice</artifactId> + <version>1.0.2</version> + <scope>test</scope> + </dependency> + + <!-- Required for testing WebConsole Support --> + <dependency> + <groupId>org.json</groupId> + <artifactId>json</artifactId> + <version>20070829</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>commons-fileupload</groupId> + <artifactId>commons-fileupload</artifactId> + <version>1.2.1</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.http.jetty</artifactId> + <version>2.2.0</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.ops4j.pax.url</groupId> + <artifactId>pax-url-wrap</artifactId> + <version>2.1.0</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.eventadmin</artifactId> + <version>1.3.2</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.metatype</artifactId> + <version>1.0.6</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.scr</artifactId> + <version>1.6.2</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>hamcrest-integration</artifactId> + <version>1.3</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.inventory</artifactId> + <version>1.0.4</version> + </dependency> + </dependencies> + + <profiles> + <!-- + copy the package such that IDEs may easily use it without + setting the system property + --> + <profile> + <id>ide</id> + <build> + <plugins> + <plugin> + <artifactId>maven-antrun-plugin</artifactId> + <executions> + <execution> + <id>log-file-create</id> + <phase>package</phase> + <goals> + <goal>run</goal> + </goals> + <configuration> + <target> + <copy file="${project.build.directory}/${project.build.finalName}.jar" tofile="${project.build.directory}/slinglogback-webconsole.jar" /> + </target> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>coverage</id> + <build> + <plugins> + <plugin> + <groupId>org.jacoco</groupId> + <artifactId>jacoco-maven-plugin</artifactId> + <executions> + <execution> + <id>prepare-agent</id> + <goals> + <goal>prepare-agent</goal> + </goals> + <configuration> + <propertyName>coverage.command</propertyName> + <includes> + <include>org.apache.sling.commons.log.logback.*</include> + </includes> + </configuration> + </execution> + <!-- Default to setup argLine required by surefire --> + <execution> + <id>prepare-agent-surefire</id> + <phase>test-compile</phase> + <goals> + <goal>prepare-agent</goal> + </goals> + <configuration> + <includes> + <include>org.apache.sling.commons.log.logback.*</include> + </includes> + </configuration> + </execution> + <execution> + <id>report</id> + <phase>post-integration-test</phase> + <goals> + <goal>report</goal> + </goals> + </execution> + <execution> + <id>check</id> + <goals> + <goal>check</goal> + </goals> + <configuration> + <!-- Disabling the check for now --> + <skip>true</skip> + <check> + <classRatio>100</classRatio> + <instructionRatio>90</instructionRatio> + <methodRatio>95</methodRatio> + <branchRatio>85</branchRatio> + <complexityRatio>85</complexityRatio> + <lineRatio>90</lineRatio> + </check> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> +</profiles> +</project> diff --git a/src/main/appended-resources/META-INF/LICENSE b/src/main/appended-resources/META-INF/LICENSE new file mode 100644 index 0000000..3378bfb --- /dev/null +++ b/src/main/appended-resources/META-INF/LICENSE @@ -0,0 +1,244 @@ + + +APACHE SLING COMMONS LOG SUBCOMPONENTS: + +The Apache Sling Script Commons Log Plugin includes a number of subcomponents +with separate copyright notices and license terms. Your use of the source +code for the these subcomponents is subject to the terms and conditions +of the following licenses. + +For the Logback Core and Logback Classic + +Eclipse Public License -v 1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE +PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF +THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + a) in the case of the initial Contributor, the initial code and + documentation distributed under this Agreement, and + b) in the case of each subsequent Contributor: + i) changes to the Program, and + ii) additions to the Program; + +where such changes and/or additions to the Program originate from and are +distributed by that particular Contributor. A Contribution 'originates' +from a Contributor if it was added to the Program by such Contributor +itself or anyone acting on such Contributor's behalf. Contributions do +not include additions to the Program which: (i) are separate modules of +software distributed in conjunction with the Program under their own +license agreement, and (ii) are not derivative works of the Program. + +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents" mean patent claims licensable by a Contributor which +are necessarily infringed by the use or sale of its Contribution alone or +when combined with the Program. + +"Program" means the Contributions distributed in accordance with this +Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, +including all Contributors. + +2. GRANT OF RIGHTS + + a) Subject to the terms of this Agreement, each Contributor hereby + grants Recipient a non-exclusive, worldwide, royalty-free copyright + license to reproduce, prepare derivative works of, publicly display, + publicly perform, distribute and sublicense the Contribution of such + Contributor, if any, and such derivative works, in source code and + object code form. + b) Subject to the terms of this Agreement, each Contributor hereby + grants Recipient a non-exclusive, worldwide, royalty-free patent + license under Licensed Patents to make, use, sell, offer to sell, + import and otherwise transfer the Contribution of such Contributor, + if any, in source code and object code form. This patent license + shall apply to the combination of the Contribution and the Program + if, at the time the Contribution is added by the Contributor, such + addition of the Contribution causes such combination to be covered + by the Licensed Patents. The patent license shall not apply to any + other combinations which include the Contribution. No hardware per + se is licensed hereunder. + c) Recipient understands that although each Contributor grants the + licenses to its Contributions set forth herein, no assurances are + provided by any Contributor that the Program does not infringe the + patent or other intellectual property rights of any other entity. + Each Contributor disclaims any liability to Recipient for claims + brought by any other entity based on infringement of intellectual + property rights or otherwise. As a condition to exercising the + rights and licenses granted hereunder, each Recipient hereby + assumes sole responsibility to secure any other intellectual + property rights needed, if any. For example, if a third party + patent license is required to allow Recipient to distribute the + Program, it is Recipient's responsibility to acquire that license + before distributing the Program. + d) Each Contributor represents that to its knowledge it has sufficient + copyright rights in its Contribution, if any, to grant the copyright + license set forth in this Agreement. + +3. REQUIREMENTS + +A Contributor may choose to distribute the Program in object code form +under its own license agreement, provided that: + + a) it complies with the terms and conditions of this Agreement; and + b) its license agreement: + i) effectively disclaims on behalf of all Contributors all warranties + and conditions, express and implied, including warranties or + conditions of title and non-infringement, and implied warranties or + conditions of merchantability and fitness for a particular purpose; + ii) effectively excludes on behalf of all Contributors all liability + for damages, including direct, indirect, special, incidental and + consequential damages, such as lost profits; + iii) states that any provisions which differ from this Agreement are + offered by that Contributor alone and not by any other party; and + iv) states that source code for the Program is available from such + Contributor, and informs licensees how to obtain it in a reasonable + manner on or through a medium customarily used for software exchange. + +When the Program is made available in source code form: + + a) it must be made available under this Agreement; and + b) a copy of this Agreement must be included with each copy of the + Program. + +Contributors may not remove or alter any copyright notices contained +within the Program. + +Each Contributor must identify itself as the originator of its +Contribution, if any, in a manner that reasonably allows subsequent +Recipients to identify the originator of the Contribution. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities +with respect to end users, business partners and the like. While this +license is intended to facilitate the commercial use of the Program, the +Contributor who includes the Program in a commercial product offering +should do so in a manner which does not create potential liability for +other Contributors. Therefore, if a Contributor includes the Program in a +commercial product offering, such Contributor ("Commercial Contributor") +hereby agrees to defend and indemnify every other Contributor ("Indemnified +Contributor") against any losses, damages and costs (collectively "Losses") +arising from claims, lawsuits and other legal actions brought by a third +party against the Indemnified Contributor to the extent caused by the acts +or omissions of such Commercial Contributor in connection with its +distribution of the Program in a commercial product offering. The +obligations in this section do not apply to any claims or Losses relating +to any actual or alleged intellectual property infringement. In order to +qualify, an Indemnified Contributor must: a) promptly notify the Commercial +Contributor in writing of such claim, and b) allow the Commercial +Contributor to control, and cooperate with the Commercial Contributor in, +the defense and any related settlement negotiations. The Indemnified +Contributor may participate in any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial +product offering, Product X. That Contributor is then a Commercial +Contributor. If that Commercial Contributor then makes performance claims, +or offers warranties related to Product X, those performance claims and +warranties are such Commercial Contributor's responsibility alone. Under +this section, the Commercial Contributor would have to defend claims +against the other Contributors related to those performance claims and +warranties, and if a court requires any other Contributor to pay any +damages as a result, the Commercial Contributor must pay those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED +ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER +EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR +CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A +PARTICULAR PURPOSE. Each Recipient is solely responsible for determining +the appropriateness of using and distributing the Program and assumes all +risks associated with its exercise of rights under this Agreement, +including but not limited to the risks and costs of program errors, +compliance with applicable laws, damage to or loss of data, programs or +equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR +ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING +WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION +OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under +applicable law, it shall not affect the validity or enforceability of the +remainder of the terms of this Agreement, and without further action by +the parties hereto, such provision shall be reformed to the minimum extent +necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Program itself +(excluding combinations of the Program with other software or hardware) +infringes such Recipient's patent(s), then such Recipient's rights granted +under Section 2(b) shall terminate as of the date such litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it fails to +comply with any of the material terms or conditions of this Agreement and +does not cure such failure in a reasonable period of time after becoming +aware of such noncompliance. If all Recipient's rights under this Agreement +terminate, Recipient agrees to cease use and distribution of the Program as +soon as reasonably practicable. However, Recipient's obligations under this +Agreement and any licenses granted by Recipient relating to the Program +shall continue and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, but +in order to avoid inconsistency the Agreement is copyrighted and may only +be modified in the following manner. The Agreement Steward reserves the +right to publish new versions (including revisions) of this Agreement from +time to time. No one other than the Agreement Steward has the right to +modify this Agreement. The Eclipse Foundation is the initial Agreement +Steward. The Eclipse Foundation may assign the responsibility to serve as +the Agreement Steward to a suitable separate entity. Each new version of +the Agreement will be given a distinguishing version number. The Program +(including Contributions) may always be distributed subject to the version +of the Agreement under which it was received. In addition, after a new +version of the Agreement is published, Contributor may elect to distribute +the Program (including its Contributions) under the new version. Except as +expressly stated in Sections 2(a) and 2(b) above, Recipient receives no +rights or licenses to the intellectual property of any Contributor under +this Agreement, whether expressly, by implication, estoppel or otherwise. +All rights in the Program not expressly granted under this Agreement are +reserved. + +This Agreement is governed by the laws of the State of New York and the +intellectual property laws of the United States of America. No party to +this Agreement will bring a legal action under this Agreement more than +one year after the cause of action arose. Each party waives its rights to +a jury trial in any resulting litigation. + + +For JUL-to-SLF4J Bridge + +Copyright (c) 2004-2013 QOS.ch +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject +to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/src/main/appended-resources/META-INF/NOTICE b/src/main/appended-resources/META-INF/NOTICE new file mode 100644 index 0000000..2fed022 --- /dev/null +++ b/src/main/appended-resources/META-INF/NOTICE @@ -0,0 +1,12 @@ +This product includes software from http://logback.qos.ch +Copyright (C) 1999-2012, QOS.ch +Licensed under the EPL License http://logback.qos.ch/license.html + +This product includes software from http://code.google.com/p/google-code-prettify/ +Licensed under the Apache License 2.0. + +This product includes software from http://www.slf4j.org/ +Licensed under the MIT License + +This product includes software from https://github.com/dyve/jquery-autocomplete +Licensed under the Apache License 2.0. diff --git a/src/main/java/org/apache/sling/commons/log/webconsole/internal/Activator.java b/src/main/java/org/apache/sling/commons/log/webconsole/internal/Activator.java new file mode 100644 index 0000000..dd8c2f6 --- /dev/null +++ b/src/main/java/org/apache/sling/commons/log/webconsole/internal/Activator.java @@ -0,0 +1,57 @@ +/* + * 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.log.webconsole.internal; + +import org.apache.sling.commons.log.logback.webconsole.LogPanel; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.util.tracker.ServiceTracker; + +public class Activator implements BundleActivator { + private ServiceTracker<LogPanel, LogWebConsolePlugin> panelTracker; + + @Override + public void start(BundleContext context) throws Exception { + panelTracker = new ServiceTracker<LogPanel, LogWebConsolePlugin>(context, LogPanel.class, null) { + @Override + public LogWebConsolePlugin addingService(ServiceReference<LogPanel> reference) { + LogPanel panel = context.getService(reference); + LogWebConsolePlugin plugin = new LogWebConsolePlugin(panel); + plugin.register(context); + return plugin; + } + + @Override + public void removedService(ServiceReference<LogPanel> reference, LogWebConsolePlugin plugin) { + plugin.unregister(); + context.ungetService(reference); + } + }; + panelTracker.open(); + } + + @Override + public void stop(BundleContext context) throws Exception { + if (panelTracker != null) { + panelTracker.close(); + } + } +} diff --git a/src/main/java/org/apache/sling/commons/log/webconsole/internal/LogWebConsolePlugin.java b/src/main/java/org/apache/sling/commons/log/webconsole/internal/LogWebConsolePlugin.java new file mode 100644 index 0000000..74084a3 --- /dev/null +++ b/src/main/java/org/apache/sling/commons/log/webconsole/internal/LogWebConsolePlugin.java @@ -0,0 +1,121 @@ +/* + * 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.log.webconsole.internal; + +import java.io.IOException; +import java.io.PrintWriter; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.felix.webconsole.SimpleWebConsolePlugin; +import org.apache.sling.commons.log.logback.webconsole.LogPanel; +import org.apache.sling.commons.log.logback.webconsole.LoggerConfig; +import org.apache.sling.commons.log.logback.webconsole.TailerOptions; +import org.apache.sling.commons.osgi.PropertiesUtil; + +import static org.apache.sling.commons.log.logback.webconsole.LogPanel.APP_ROOT; +import static org.apache.sling.commons.log.logback.webconsole.LogPanel.PARAM_APPENDER_NAME; +import static org.apache.sling.commons.log.logback.webconsole.LogPanel.PARAM_NUM_OF_LINES; +import static org.apache.sling.commons.log.logback.webconsole.LogPanel.PATH_TAILER; + +public class LogWebConsolePlugin extends SimpleWebConsolePlugin { + private static final String RES_LOC = LogPanel.APP_ROOT + "/res/ui"; + + private static final String[] CSS_REFS = { + RES_LOC + "/jquery.autocomplete.css", + RES_LOC + "/prettify.css", + RES_LOC + "/log.css", + }; + + private final LogPanel panel; + + public LogWebConsolePlugin(LogPanel panel) { + super(LogPanel.APP_ROOT, "Log Support", "Sling", CSS_REFS); + this.panel = panel; + } + + @Override + protected void renderContent(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + final PrintWriter pw = resp.getWriter(); + final String consoleAppRoot = getAppRoot(req); + + if (req.getPathInfo() != null) { + if (req.getPathInfo().endsWith(PATH_TAILER)) { + String appenderName = req.getParameter(PARAM_APPENDER_NAME); + addNoSniffHeader(resp); + if (appenderName == null) { + pw.printf("Provide appender name via [%s] request parameter%n", PARAM_APPENDER_NAME); + return; + } + int numOfLines = PropertiesUtil.toInteger(req.getParameter(PARAM_NUM_OF_LINES), 0); + TailerOptions opts = new TailerOptions(numOfLines); + panel.tail(pw, appenderName, opts); + return; + } + } + + panel.render(pw, consoleAppRoot); + } + + @Override + protected boolean isHtmlRequest(HttpServletRequest request) { + return !request.getRequestURI().endsWith(PATH_TAILER); + } + + @Override + protected void doPost(final HttpServletRequest req, final HttpServletResponse resp) + throws ServletException, IOException { + // check if a configuration should be deleted + boolean isDelete = req.getParameter("delete") != null; + // get the configuration pid + String pid = req.getParameter("pid"); + if (isDelete) { + // in delete mode remove the logger with the given pid + panel.deleteLoggerConfig(pid); + } else { + // get the logger parameters and configure the logger + // if the given pid is empty a new logger with be created + String logger = req.getParameter("logger"); + String logLevel = req.getParameter("loglevel"); + String logFile = req.getParameter("logfile"); + boolean additive = PropertiesUtil.toBoolean(req.getParameter("logAdditive"), false); + String[] loggers = req.getParameterValues("logger"); + if (null != logger) { + LoggerConfig config = new LoggerConfig(pid, logLevel, loggers, logFile, additive); + panel.createLoggerConfig(config); + } + } + + // send the redirect back to the logpanel + final String consoleAppRoot = getAppRoot(req); + resp.sendRedirect(consoleAppRoot + "/" + APP_ROOT); + } + + private static String getAppRoot(HttpServletRequest req) { + return (String) req.getAttribute("felix.webconsole.appRoot"); + } + + private static void addNoSniffHeader(HttpServletResponse resp) { + resp.setHeader("X-Content-Type-Options", "nosniff"); + } +} diff --git a/src/main/resources/res/ui/jquery.autocomplete.css b/src/main/resources/res/ui/jquery.autocomplete.css new file mode 100644 index 0000000..9a95dc7 --- /dev/null +++ b/src/main/resources/res/ui/jquery.autocomplete.css @@ -0,0 +1,38 @@ +/** + * @fileOverview CSS for jquery-autocomplete, the jQuery Autocompleter + * @author <a href="mailto:[email protected]">Dylan Verheul</a> + * @license MIT | GPL | Apache 2.0, see LICENSE.txt + * @see https://github.com/dyve/jquery-autocomplete + */ +.acResults { + padding: 0px; + border: 1px solid WindowFrame; + background-color: Window; + overflow: hidden; +} + +.acResults ul { + margin: 0px; + padding: 0px; + list-style-position: outside; + list-style: none; +} + +.acResults ul li { + margin: 0px; + padding: 2px 5px; + cursor: pointer; + display: block; + font: menu; + font-size: 12px; + overflow: hidden; +} + +.acLoading { + background : url('indicator.gif') right center no-repeat; +} + +.acSelect { + background-color: Highlight; + color: HighlightText; +} \ No newline at end of file diff --git a/src/main/resources/res/ui/jquery.autocomplete.min.js b/src/main/resources/res/ui/jquery.autocomplete.min.js new file mode 100644 index 0000000..4bb8d58 --- /dev/null +++ b/src/main/resources/res/ui/jquery.autocomplete.min.js @@ -0,0 +1,9 @@ +/** + * @fileOverview jquery-autocomplete, the jQuery Autocompleter + * @author <a href="mailto:[email protected]">Dylan Verheul</a> + * @version 2.4.4 + * @requires jQuery 1.6+ + * @license MIT | GPL | Apache 2.0, see LICENSE.txt + * @see https://github.com/dyve/jquery-autocomplete + */ +(function($){"use strict";$.fn.autocomplete=function(options){var url;if(arguments.length>1){url=options;options=arguments[1];options.url=url}else if(typeof options==="string"){url=options;options={url:url}}var opts=$.extend({},$.fn.autocomplete.defaults,options);return this.each(function(){var $this=$(this);$this.data("autocompleter",new $.Autocompleter($this,$.meta?$.extend({},opts,$this.data()):opts))})};$.fn.autocomplete.defaults={inputClass:"acInput",loadingClass:"acLoading",results [...] \ No newline at end of file diff --git a/src/main/resources/res/ui/log.css b/src/main/resources/res/ui/log.css new file mode 100644 index 0000000..55618bb --- /dev/null +++ b/src/main/resources/res/ui/log.css @@ -0,0 +1,22 @@ +/* + * 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. + */ +.warn { font-weight: bold; color: #FF6600;} +.error { font-weight: bold; color: #CC0000;} + +td.date { text-align: right; font-family: courier, monospace; font-size: smaller; } +td.level { text-align: right; } +td.exception { background: #A2AEE8; white-space: pre; font-family: courier, monospace;} \ No newline at end of file diff --git a/src/main/resources/res/ui/prettify.css b/src/main/resources/res/ui/prettify.css new file mode 100644 index 0000000..d44b3a2 --- /dev/null +++ b/src/main/resources/res/ui/prettify.css @@ -0,0 +1 @@ +.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding [...] \ No newline at end of file diff --git a/src/main/resources/res/ui/prettify.js b/src/main/resources/res/ui/prettify.js new file mode 100644 index 0000000..7b99049 --- /dev/null +++ b/src/main/resources/res/ui/prettify.js @@ -0,0 +1,30 @@ +!function(){var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; +(function(){function S(a){function d(e){var b=e.charCodeAt(0);if(b!==92)return b;var a=e.charAt(1);return(b=r[a])?b:"0"<=a&&a<="7"?parseInt(e.substring(1),8):a==="u"||a==="x"?parseInt(e.substring(2),16):e.charCodeAt(1)}function g(e){if(e<32)return(e<16?"\\x0":"\\x")+e.toString(16);e=String.fromCharCode(e);return e==="\\"||e==="-"||e==="]"||e==="^"?"\\"+e:e}function b(e){var b=e.substring(1,e.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/ [...] +b[0]==="^",c=["["];a&&c.push("^");for(var a=a?1:0,f=b.length;a<f;++a){var h=b[a];if(/\\[bdsw]/i.test(h))c.push(h);else{var h=d(h),l;a+2<f&&"-"===b[a+1]?(l=d(b[a+2]),a+=2):l=h;e.push([h,l]);l<65||h>122||(l<65||h>90||e.push([Math.max(65,h)|32,Math.min(l,90)|32]),l<97||h>122||e.push([Math.max(97,h)&-33,Math.min(l,122)&-33]))}}e.sort(function(e,a){return e[0]-a[0]||a[1]-e[1]});b=[];f=[];for(a=0;a<e.length;++a)h=e[a],h[0]<=f[1]+1?f[1]=Math.max(f[1],h[1]):b.push(f=h);for(a=0;a<b.length;++a)h=b [...] +h[1]>h[0]&&(h[1]+1>h[0]&&c.push("-"),c.push(g(h[1])));c.push("]");return c.join("")}function s(e){for(var a=e.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),c=a.length,d=[],f=0,h=0;f<c;++f){var l=a[f];l==="("?++h:"\\"===l.charAt(0)&&(l=+l.substring(1))&&(l<=h?d[l]=-1:a[f]=g(l))}for(f=1;f<d.length;++f)-1===d[f]&&(d[f]=++x);for(h=f=0;f<c;++f)l=a[f],l==="("?(++h,d[h]||(a[f]="(?:")):"\\"===l.charAt(0)&&(l=+l.substring(1) [...] +(a[f]="\\"+d[l]);for(f=0;f<c;++f)"^"===a[f]&&"^"!==a[f+1]&&(a[f]="");if(e.ignoreCase&&m)for(f=0;f<c;++f)l=a[f],e=l.charAt(0),l.length>=2&&e==="["?a[f]=b(l):e!=="\\"&&(a[f]=l.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return a.join("")}for(var x=0,m=!1,j=!1,k=0,c=a.length;k<c;++k){var i=a[k];if(i.ignoreCase)j=!0;else if(/[a-z]/i.test(i.source.replace(/\\u[\da-f]{4}|\\x[\da-f]{2}|\\[^UXux]/gi,""))){m=!0;j=!1;break}}for(var r={b:8,t:9, [...] +f:12,r:13},n=[],k=0,c=a.length;k<c;++k){i=a[k];if(i.global||i.multiline)throw Error(""+i);n.push("(?:"+s(i)+")")}return RegExp(n.join("|"),j?"gi":"g")}function T(a,d){function g(a){var c=a.nodeType;if(c==1){if(!b.test(a.className)){for(c=a.firstChild;c;c=c.nextSibling)g(c);c=a.nodeName.toLowerCase();if("br"===c||"li"===c)s[j]="\n",m[j<<1]=x++,m[j++<<1|1]=a}}else if(c==3||c==4)c=a.nodeValue,c.length&&(c=d?c.replace(/\r\n?/g,"\n"):c.replace(/[\t\n\r ]+/g," "),s[j]=c,m[j<<1]=x,x+=c.length,m [...] +a)}var b=/(?:^|\s)nocode(?:\s|$)/,s=[],x=0,m=[],j=0;g(a);return{a:s.join("").replace(/\n$/,""),d:m}}function H(a,d,g,b){d&&(a={a:d,e:a},g(a),b.push.apply(b,a.g))}function U(a){for(var d=void 0,g=a.firstChild;g;g=g.nextSibling)var b=g.nodeType,d=b===1?d?a:g:b===3?V.test(g.nodeValue)?a:d:d;return d===a?void 0:d}function C(a,d){function g(a){for(var j=a.e,k=[j,"pln"],c=0,i=a.a.match(s)||[],r={},n=0,e=i.length;n<e;++n){var z=i[n],w=r[z],t=void 0,f;if(typeof w==="string")f=!1;else{var h=b[z.c [...] +if(h)t=z.match(h[1]),w=h[0];else{for(f=0;f<x;++f)if(h=d[f],t=z.match(h[1])){w=h[0];break}t||(w="pln")}if((f=w.length>=5&&"lang-"===w.substring(0,5))&&!(t&&typeof t[1]==="string"))f=!1,w="src";f||(r[z]=w)}h=c;c+=z.length;if(f){f=t[1];var l=z.indexOf(f),B=l+f.length;t[2]&&(B=z.length-t[2].length,l=B-f.length);w=w.substring(5);H(j+h,z.substring(0,l),g,k);H(j+h+l,f,I(w,f),k);H(j+h+B,z.substring(B),g,k)}else k.push(j+h,w)}a.g=k}var b={},s;(function(){for(var g=a.concat(d),j=[],k={},c=0,i=g.le [...] +g[c],n=r[3];if(n)for(var e=n.length;--e>=0;)b[n.charAt(e)]=r;r=r[1];n=""+r;k.hasOwnProperty(n)||(j.push(r),k[n]=q)}j.push(/[\S\s]/);s=S(j)})();var x=d.length;return g}function v(a){var d=[],g=[];a.tripleQuotedStrings?d.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?d.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)| [...] +q,"'\"`"]):d.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&g.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var b=a.hashComments;b&&(a.cStyleComments?(b>1?d.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):d.push(["com",/^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),g.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h(?:h|pp|\+\+)?|[a-z]\w*)>/,q])):d. [...] +/^#[^\n\r]*/,q,"#"]));a.cStyleComments&&(g.push(["com",/^\/\/[^\n\r]*/,q]),g.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));if(b=a.regexLiterals){var s=(b=b>1?"":"\n\r")?".":"[\\S\\s]";g.push(["lang-regex",RegExp("^(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[+\\-]=|->|\\/=?|::?|<<?=?|>>?>?=?|,|;|\\?|@|\\[|~|{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*("+("/(?=[^/*"+b+"])(?:[^/\\x5B\\x5C"+b+"]|\\x5C"+s+"|\\x5B(?:[^\\x5C\\x5D"+b+ [...] +s+")*(?:\\x5D|$))+/")+")")])}(b=a.types)&&g.push(["typ",b]);b=(""+a.keywords).replace(/^ | $/g,"");b.length&&g.push(["kwd",RegExp("^(?:"+b.replace(/[\s,]+/g,"|")+")\\b"),q]);d.push(["pln",/^\s+/,q," \r\n\t\u00a0"]);b="^.[^\\s\\w.$@'\"`/\\\\]*";a.regexLiterals&&(b+="(?!s*/)");g.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["p [...] +q],["pun",RegExp(b),q]);return C(d,g)}function J(a,d,g){function b(a){var c=a.nodeType;if(c==1&&!x.test(a.className))if("br"===a.nodeName)s(a),a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)b(a);else if((c==3||c==4)&&g){var d=a.nodeValue,i=d.match(m);if(i)c=d.substring(0,i.index),a.nodeValue=c,(d=d.substring(i.index+i[0].length))&&a.parentNode.insertBefore(j.createTextNode(d),a.nextSibling),s(a),c||a.parentNode.removeChild(a)}}function s(a){function b [...] +c?a.cloneNode(!1):a,e=a.parentNode;if(e){var e=b(e,1),g=a.nextSibling;e.appendChild(d);for(var i=g;i;i=g)g=i.nextSibling,e.appendChild(i)}return d}for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),d;(d=a.parentNode)&&d.nodeType===1;)a=d;c.push(a)}for(var x=/(?:^|\s)nocode(?:\s|$)/,m=/\r\n?|\n/,j=a.ownerDocument,k=j.createElement("li");a.firstChild;)k.appendChild(a.firstChild);for(var c=[k],i=0;i<c.length;++i)b(c[i]);d===(d|0)&&c[0].setAttribute("value",d);var [...] +r.className="linenums";for(var d=Math.max(0,d-1|0)||0,i=0,n=c.length;i<n;++i)k=c[i],k.className="L"+(i+d)%10,k.firstChild||k.appendChild(j.createTextNode("\u00a0")),r.appendChild(k);a.appendChild(r)}function p(a,d){for(var g=d.length;--g>=0;){var b=d[g];F.hasOwnProperty(b)?D.console&&console.warn("cannot override language handler %s",b):F[b]=a}}function I(a,d){if(!a||!F.hasOwnProperty(a))a=/^\s*</.test(d)?"default-markup":"default-code";return F[a]}function K(a){var d=a.h;try{var g=T(a.c [...] +a.a=b;a.d=g.d;a.e=0;I(d,b)(a);var s=/\bMSIE\s(\d+)/.exec(navigator.userAgent),s=s&&+s[1]<=8,d=/\n/g,x=a.a,m=x.length,g=0,j=a.d,k=j.length,b=0,c=a.g,i=c.length,r=0;c[i]=m;var n,e;for(e=n=0;e<i;)c[e]!==c[e+2]?(c[n++]=c[e++],c[n++]=c[e++]):e+=2;i=n;for(e=n=0;e<i;){for(var p=c[e],w=c[e+1],t=e+2;t+2<=i&&c[t+1]===w;)t+=2;c[n++]=p;c[n++]=w;e=t}c.length=n;var f=a.c,h;if(f)h=f.style.display,f.style.display="none";try{for(;b<k;){var l=j[b+2]||m,B=c[r+2]||m,t=Math.min(l,B),A=j[b+1],G;if(A.nodeType! [...] +t))){s&&(G=G.replace(d,"\r"));A.nodeValue=G;var L=A.ownerDocument,o=L.createElement("span");o.className=c[r+1];var v=A.parentNode;v.replaceChild(o,A);o.appendChild(A);g<l&&(j[b+1]=A=L.createTextNode(x.substring(t,l)),v.insertBefore(A,o.nextSibling))}g=t;g>=l&&(b+=2);g>=B&&(r+=2)}}finally{if(f)f.style.display=h}}catch(u){D.console&&console.log(u&&u.stack||u)}}var D=window,y=["break,continue,do,else,for,if,return,while"],E=[[y,"auto,case,char,const,default,double,enum,extern,float,goto,inl [...] +"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],M=[E,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,delegate,dynamic_cast,explicit,export,friend,generic,late_check,mutable,namespace,nullptr,property,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],N=[E,"abstract,assert,boolean,byte,extends,final,finally,implements,import,instanceof,interface,null,native,pack [...] +O=[N,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,internal,into,is,let,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var,virtual,where"],E=[E,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],P=[y,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,wi [...] +Q=[y,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],W=[y,"as,assert,const,copy,drop,enum,extern,fail,false,fn,impl,let,log,loop,match,mod,move,mut,priv,pub,pure,ref,self,static,struct,true,trait,type,unsafe,use"],y=[y,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],R=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map [...] +V=/\S/,X=v({keywords:[M,O,E,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",P,Q,y],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),F={};p(X,["default-code"]);p(C([],[["pln",/^[^<?]+/],["dec",/^<!\w[^>]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%? [...] +/^<xmp\b[^>]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);p(C([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["p [...] +["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css",/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);p(C([],[["atv",/^[\S\s]+/]]),["uq.val"]);p(v({keywords:M,hashComments:!0,cStyleComments:!0,types:R}),["c","cc","cpp","cxx","cyc","m"]);p(v({keywords:"null,true,false"}),["json"]);p(v({keywords:O,hashComments:!0,cStyleComments:!0,verbatimString [...] +["cs"]);p(v({keywords:N,cStyleComments:!0}),["java"]);p(v({keywords:y,hashComments:!0,multiLineStrings:!0}),["bash","bsh","csh","sh"]);p(v({keywords:P,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),["cv","py","python"]);p(v({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:2}),["perl","pl", [...] +hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb","ruby"]);p(v({keywords:E,cStyleComments:!0,regexLiterals:!0}),["javascript","js"]);p(v({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,throw,true,try,unless,until,when,while,yes",hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);p(v({keywords:W,cStyleComments:!0,multilineStrings:!0}),["rc", [...] +p(C([],[["str",/^[\S\s]+/]]),["regex"]);var Y=D.PR={createSimpleLexer:C,registerLangHandler:p,sourceDecorator:v,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ",prettyPrintOne:D.prettyPrintOne=function(a,d,g){var b=document.createElement("div");b.innerHTML="<pre>"+a+"</pre>";b=b.firstChild;g&&J(b,g,!0);K({h:d,j [...] +return b.innerHTML},prettyPrint:D.prettyPrint=function(a,d){function g(){for(var b=D.PR_SHOULD_USE_CONTINUATION?c.now()+250:Infinity;i<p.length&&c.now()<b;i++){for(var d=p[i],j=h,k=d;k=k.previousSibling;){var m=k.nodeType,o=(m===7||m===8)&&k.nodeValue;if(o?!/^\??prettify\b/.test(o):m!==3||/\S/.test(k.nodeValue))break;if(o){j={};o.replace(/\b(\w+)=([\w%+\-.:]+)/g,function(a,b,c){j[b]=c});break}}k=d.className;if((j!==h||e.test(k))&&!v.test(k)){m=!1;for(o=d.parentNode;o;o=o.parentNode)if(f. [...] +o.className&&e.test(o.className)){m=!0;break}if(!m){d.className+=" prettyprinted";m=j.lang;if(!m){var m=k.match(n),y;if(!m&&(y=U(d))&&t.test(y.tagName))m=y.className.match(n);m&&(m=m[1])}if(w.test(d.tagName))o=1;else var o=d.currentStyle,u=s.defaultView,o=(o=o?o.whiteSpace:u&&u.getComputedStyle?u.getComputedStyle(d,q).getPropertyValue("white-space"):0)&&"pre"===o.substring(0,3);u=j.linenums;if(!(u=u==="true"||+u))u=(u=k.match(/\blinenums\b(?::(\d+))?/))?u[1]&&u[1].length?+u[1]:!0:!1;u&&J [...] +{h:m,c:d,j:u,i:o};K(r)}}}i<p.length?setTimeout(g,250):"function"===typeof a&&a()}for(var b=d||document.body,s=b.ownerDocument||document,b=[b.getElementsByTagName("pre"),b.getElementsByTagName("code"),b.getElementsByTagName("xmp")],p=[],m=0;m<b.length;++m)for(var j=0,k=b[m].length;j<k;++j)p.push(b[m][j]);var b=q,c=Date;c.now||(c={now:function(){return+new Date}});var i=0,r,n=/\blang(?:uage)?-([\w.]+)(?!\S)/,e=/\bprettyprint\b/,v=/\bprettyprinted\b/,w=/pre|xmp/i,t=/^code$/i,f=/^(?:pre|code [...] +h={};g()}};typeof define==="function"&&define.amd&&define("google-code-prettify",[],function(){return Y})})();}() diff --git a/src/main/resources/res/ui/resourceresolver.css b/src/main/resources/res/ui/resourceresolver.css new file mode 100644 index 0000000..789d578 --- /dev/null +++ b/src/main/resources/res/ui/resourceresolver.css @@ -0,0 +1,21 @@ +/* + * 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. + */ + +tr.content.duplicate td.content { + color: grey; + text-decoration: line-through; +} \ No newline at end of file diff --git a/src/main/resources/res/ui/slinglog.js b/src/main/resources/res/ui/slinglog.js new file mode 100644 index 0000000..3eb5d55 --- /dev/null +++ b/src/main/resources/res/ui/slinglog.js @@ -0,0 +1,167 @@ +/* + * 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. + */ +/** + * Removes the editor (toggles all displayfields/editables). + */ +function removeEditor(row) { + $(row).find(".loggers").toggle(); + $(row).find(".logAdditive").toggle(); + $(row).find(".logLevels").toggle(); + $(row).find(".logFile").toggle(); + $(row).find(".configureLink").toggle(); + $(row).find(".editElement").remove(); + $(row).removeClass("currentEditor"); +} + +/** + * Turns the loglevel element into an selectfield (current loglevel is selected). + */ +function addLogLevelSelect(row) { + var logLevelElement = $(row).find(".logLevels"); + // get the current loglevel + var currentLogLevel = logLevelElement.attr("data-currentloglevel"); + if(!currentLogLevel) { + // convenience default for new loggers + currentLogLevel = "INFO"; + } + // get all available loglevels (present in the "newlogger" element) + var allLogLevels = $("#allLogLevels").attr("data-loglevels").split(","); + var select = $('<select class="editElement" name="loglevel"></select>'); + $.each(allLogLevels, function(index, logLevel) { + select.append('<option'+(logLevel == currentLogLevel ? ' selected="selected"' : '')+'>'+logLevel+'</option>'); + }); + logLevelElement.after(select); + logLevelElement.toggle(); +} + +/** + * Adds a new editable logger for the given loggerelement (with controls for adding/removing). + * @param loggersElement logger element + * @param loggerName name of the logger + */ +function addLogger(loggersElement, loggerName) { + var addButton = $('<input type="submit" name="add" class="ui-state-default ui-corner-all" value="+" style="width:5%;" />'); + addButton.bind("click", function() { + addLogger($(this).parent(), ""); + return false; + }); + var removeButton = $('<input type="submit" class="ui-state-default ui-corner-all" name="remove" value="-" style="width:5%;" />'); + removeButton.bind("click", function() { + $(this).parent().remove(); + return false; + }); + var loggerField = $('<input type="text" name="logger" class="loggerField ui-state-default ui-corner-all inputText" value="'+loggerName+'" autocomplete="off" style="width:89%;" />'); + // add the autocomplete with the array of all loggers + loggerField.autocomplete({ + data: loggers + }); + var logger = $('<div class="editElement"></div>').append(loggerField, addButton, removeButton); + loggersElement.after(logger); +} + +/** + * Make checkbox for additive field + */ +function addAdditive(row) { + var additiveElement = $(row).find(".logAdditive"); + var currentAdditivity = additiveElement.attr("data-currentAdditivity"); + if(!currentAdditivity) { + // default additivity is false + currentAdditivity = "false"; + } + + var checked = currentAdditivity == "true" ? "checked" : ""; + additiveElement.after('<input class="editElement ui-state-default" type="checkbox" name="logAdditive" value="true" '+ checked +' />') + additiveElement.toggle(); +} + +/** + * Turns the logger elements into inputfields (with controls). + */ +function addLoggers(row) { + var loggersElement = $(row).find(".loggers"); + var loggers = loggersElement.find(".logger"); + if(loggers.length == 0) { + addLogger(loggersElement, ""); + } + $.each(loggers, function(index, logger) { + addLogger(loggersElement, $(logger).html()); + }); + loggersElement.toggle(); +} + +/** + * Turns the logfile element into an inputfield. + */ +function addLogFile(row) { + var logFileElement = $(row).find(".logFile"); + var logFile = ""; + if(logFileElement.length > 0) { + logFile = $(logFileElement).html(); + } + if (logFile.length == 0) { + // no logfile -> new logger -> take default + logFile = $("#defaultLogfile").attr("data-defaultlogfile"); + } + logFileElement.after('<input style="width:100%" class="editElement ui-state-default ui-corner-all inputText" type="text" name="logfile" value="'+logFile+'" />'); + logFileElement.toggle(); +} + +/** + * Activates the logger configurator (called by clicking the configure link). + * Turns all display fields in the logger row containing the configure link into edit fields. + * @param button configure link + */ +function configureLogger(button) { + var configureLink = $(button.currentTarget); + var row = configureLink.parent().parent(); + var rowId = $(row).attr("id"); + // remove the current editor, since we have only one form only one editor can be active the same time + removeEditor($(".currentEditor")); + // add class as marker (id is already used for pid) + row.addClass("currentEditor"); + // add the editables + addLogLevelSelect(row); + addAdditive(row); + addLoggers(row); + addLogFile(row); + // add controls + var hiddenField = $('<input class="editElement" type="hidden" name="pid" value="'+(rowId != 'newlogger' ? rowId : '')+'" />'); + var saveButton = $('<input class="editElement" type="submit" name="save" value="Save" />'); + var cancelButton=$('<input class="editElement" type="submit" value="Cancel" />'); + cancelButton.bind("click", function() { + var row = $(this).parent().parent(); + removeEditor(row); + return false; + }); + var deleteButton = $('<input class="editElement" type="submit" name="delete" value="Remove Logger" />'); + configureLink.after(saveButton, cancelButton, hiddenField); + if (rowId !== "newlogger") { + // add a delete buttons for existing loggers + cancelButton.after(deleteButton); + } + configureLink.toggle(); + // prevent click on link + return false; +} + +/** + * Initializes the log panel. + */ +function initializeSlingLogPanel() { + $("#loggerConfig").find(".configureLink").bind("click", configureLogger); +} diff --git a/src/main/resources/res/ui/styles.css b/src/main/resources/res/ui/styles.css new file mode 100644 index 0000000..94595e7 --- /dev/null +++ b/src/main/resources/res/ui/styles.css @@ -0,0 +1,35 @@ +/* + * 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. + */ +ol.servlets { + margin-top: 10px; +} +ol.servlets li { + list-style: decimal; + margin-left: 20px; +} + +.content dt { + font-weight: bold; +} +.content dd { + margin-left: 10px; +} + +.content em { + text-style:italic; + color:gray; +} \ No newline at end of file diff --git a/src/test/java/org/apache/sling/commons/log/webconsole/ITLogWebConsolePlugin.java b/src/test/java/org/apache/sling/commons/log/webconsole/ITLogWebConsolePlugin.java new file mode 100644 index 0000000..4b837ac --- /dev/null +++ b/src/test/java/org/apache/sling/commons/log/webconsole/ITLogWebConsolePlugin.java @@ -0,0 +1,56 @@ +/* + * 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.log.webconsole; + +import javax.inject.Inject; +import javax.servlet.Servlet; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.ops4j.pax.exam.junit.PaxExam; +import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy; +import org.ops4j.pax.exam.spi.reactors.PerClass; +import org.ops4j.pax.exam.util.Filter; + +import static org.junit.Assert.assertNotNull; + +@RunWith(PaxExam.class) +@ExamReactorStrategy(PerClass.class) +public class ITLogWebConsolePlugin extends LogTestBase { + + @Inject() + @Filter(value = "(&(felix.webconsole.label=slinglog)(felix.webconsole.title=*))", timeout = 10000) + private Servlet servlet; + + static { + // uncomment to enable debugging of this test class + // paxRunnerVmOption = DEBUG_VM_OPTION; + + } + + /** + * Checks the presence of plugin servlet if Servlet API is present + */ + @Test + public void testLogPlugin() throws Exception { + assertNotNull(servlet); + } + +} diff --git a/src/test/java/org/apache/sling/commons/log/webconsole/ITWebConsoleRemote.java b/src/test/java/org/apache/sling/commons/log/webconsole/ITWebConsoleRemote.java new file mode 100644 index 0000000..30289a2 --- /dev/null +++ b/src/test/java/org/apache/sling/commons/log/webconsole/ITWebConsoleRemote.java @@ -0,0 +1,146 @@ +/* + * 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.log.webconsole; + +import java.io.File; +import java.io.IOException; + +import com.gargoylesoftware.htmlunit.DefaultCredentialsProvider; +import com.gargoylesoftware.htmlunit.Page; +import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import org.apache.commons.io.FilenameUtils; +import org.apache.sling.commons.log.webconsole.remote.WebConsoleTestActivator; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Test; +import org.ops4j.pax.exam.ExamSystem; +import org.ops4j.pax.exam.Option; +import org.ops4j.pax.exam.TestContainer; +import org.ops4j.pax.exam.spi.DefaultExamSystem; +import org.ops4j.pax.exam.spi.PaxExamRuntime; +import org.ops4j.pax.tinybundles.core.TinyBundle; +import org.osgi.framework.Constants; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.ops4j.pax.exam.CoreOptions.composite; +import static org.ops4j.pax.exam.CoreOptions.frameworkProperty; +import static org.ops4j.pax.exam.CoreOptions.provision; +import static org.ops4j.pax.tinybundles.core.TinyBundles.bundle; +import static org.ops4j.pax.tinybundles.core.TinyBundles.withBnd; + +public class ITWebConsoleRemote extends LogTestBase { + + private static final String PLUGIN_SUFFIX = "slinglog"; + + private static final String PRINTER_SUFFIX = "status-slinglogs"; + + private static TestContainer testContainer; + + private WebClient webClient; + + @Override + protected Option addPaxExamSpecificOptions() { + return null; + } + + @Override + protected Option addExtraOptions() { + return composite( + frameworkProperty("org.apache.sling.commons.log.configurationFile").value( + FilenameUtils.concat(new File(".").getAbsolutePath(), "src/test/resources/test-webconsole-remote.xml")), + createWebConsoleTestBundle() + ); + } + + private Option createWebConsoleTestBundle() { + TinyBundle bundle = bundle(); + for(Class c : WebConsoleTestActivator.BUNDLE_CLASS_NAMES){ + bundle.add(c); + } + + bundle.set(Constants.BUNDLE_SYMBOLICNAME,"org.apache.sling.common.log.testbundle") + .set(Constants.BUNDLE_ACTIVATOR , WebConsoleTestActivator.class.getName()); + return provision(bundle.build(withBnd())); + + } + + @Before + public void setUp() throws IOException { + // Had to use a @Before instead of @BeforeClass as that requires a + // static method + if (testContainer == null) { + ExamSystem system = DefaultExamSystem.create(config()); + testContainer = PaxExamRuntime.createContainer(system); + testContainer.start(); + } + } + + @Before + public void prepareWebClient() { + webClient = new WebClient(); + ((DefaultCredentialsProvider) webClient.getCredentialsProvider()).addCredentials("admin", "admin"); + } + + @Test + public void testWebConsolePlugin() throws IOException { + final HtmlPage page = webClient.getPage(prepareUrl(PLUGIN_SUFFIX)); + String text = page.asText(); + + //Filter name should be part of Filter table + assertTrue(text.contains("WebConsoleTestTurboFilter")); + + //Console name should be part of console table + assertTrue(text.contains("WebConsoleTestAppender")); + + //Should show file name testremote.log + assertTrue(text.contains("testremote.log")); + } + + @Test + public void testPrinter() throws IOException { + final HtmlPage page = webClient.getPage(prepareUrl(PRINTER_SUFFIX)); + String text = page.asText(); + + //Should dump content of configured file testremote.log + //with its name + assertTrue(text.contains("testremote.log")); + } + + @Test + public void tailerHeader() throws Exception{ + Page page = webClient.getPage(prepareUrl("slinglog/tailer.txt?name=webconsoletest1.log")); + String nosniffHeader = page.getWebResponse().getResponseHeaderValue("X-Content-Type-Options"); + assertEquals("nosniff", nosniffHeader); + } + + @AfterClass + public static void tearDownClass() { + if (testContainer != null) { + testContainer.stop(); + testContainer = null; + } + } + + private static String prepareUrl(String suffix) { + return String.format("http://localhost:%s/system/console/%s", LogTestBase.getServerPort(), suffix); + } +} diff --git a/src/test/java/org/apache/sling/commons/log/webconsole/LogTestBase.java b/src/test/java/org/apache/sling/commons/log/webconsole/LogTestBase.java new file mode 100644 index 0000000..a22b3ba --- /dev/null +++ b/src/test/java/org/apache/sling/commons/log/webconsole/LogTestBase.java @@ -0,0 +1,165 @@ +/* + * 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.log.webconsole; + +import java.io.File; +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.FilenameUtils; +import org.ops4j.pax.exam.Configuration; +import org.ops4j.pax.exam.CoreOptions; +import org.ops4j.pax.exam.Option; +import org.ops4j.pax.exam.options.DefaultCompositeOption; +import org.osgi.framework.BundleContext; + +import static org.ops4j.pax.exam.CoreOptions.composite; +import static org.ops4j.pax.exam.CoreOptions.frameworkProperty; +import static org.ops4j.pax.exam.CoreOptions.junitBundles; +import static org.ops4j.pax.exam.CoreOptions.keepCaches; +import static org.ops4j.pax.exam.CoreOptions.mavenBundle; +import static org.ops4j.pax.exam.CoreOptions.options; +import static org.ops4j.pax.exam.CoreOptions.systemProperty; +import static org.ops4j.pax.exam.CoreOptions.systemTimeout; +import static org.ops4j.pax.exam.CoreOptions.workingDirectory; +import static org.ops4j.pax.exam.CoreOptions.wrappedBundle; +import static org.ops4j.pax.exam.util.PathUtils.getBaseDir; + +public abstract class LogTestBase { + @Inject + protected BundleContext bundleContext; + + // the name of the system property providing the bundle file to be installed + // and tested + protected static final String BUNDLE_JAR_SYS_PROP = "project.bundle.file"; + + // the name of the system property which captures the jococo coverage agent + // command + // if specified then agent would be specified otherwise ignored + protected static final String COVERAGE_COMMAND = "coverage.command"; + + // the default bundle jar file name + protected static final String BUNDLE_JAR_DEFAULT = "target/slinglogback-webconsole.jar"; + + // the JVM option to set to enable remote debugging + @SuppressWarnings("UnusedDeclaration") + protected static final String DEBUG_VM_OPTION = "-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=31313"; + + // the actual JVM option set, extensions may implement a static + // initializer overwriting this value to have the configuration() + // method include it when starting the OSGi framework JVM + protected static String paxRunnerVmOption = null; + + // Name of the property for port of server + public static final String HTTP_PORT_PROP = "http.port"; + + protected static String DEFAULT_PORT = "8080"; + + @Configuration + public Option[] config() throws IOException { + final String bundleFileName = System.getProperty(BUNDLE_JAR_SYS_PROP, BUNDLE_JAR_DEFAULT); + final File bundleFile = new File(bundleFileName); + if (!bundleFile.canRead()) { + throw new IllegalArgumentException("Cannot read from bundle file " + bundleFileName + " specified in the " + + BUNDLE_JAR_SYS_PROP + " system property. Try building the project first " + + "with 'mvn clean install -Pide -DskipTests'"); + } + return options( + // the current project (the bundle under test) + CoreOptions.bundle(bundleFile.toURI().toString()), + mavenBundle("org.slf4j", "slf4j-api").versionAsInProject(), + mavenBundle("org.apache.sling", "org.apache.sling.commons.logservice").versionAsInProject(), + LogTestBase.webSupport(), + mavenBundle("org.apache.sling", "org.apache.sling.commons.log").versionAsInProject(), + mavenBundle("org.apache.felix", "org.apache.felix.http.jetty").versionAsInProject(), + mavenBundle("org.apache.felix", "org.apache.felix.webconsole").versionAsInProject(), + mavenBundle("org.apache.felix", "org.apache.felix.inventory").versionAsInProject(), + mavenBundle("org.apache.felix", "org.apache.felix.metatype").versionAsInProject(), + mavenBundle("org.apache.felix", "org.apache.felix.scr").versionAsInProject(), + mavenBundle("commons-io", "commons-io").versionAsInProject(), + wrappedBundle(mavenBundle("commons-fileupload", "commons-fileupload").versionAsInProject()), + wrappedBundle(mavenBundle("org.json", "json").versionAsInProject()), + LogTestBase.configAdmin(), + addPaxExamSpecificOptions(), + addCodeCoverageOption(), addDebugOptions(), addExtraOptions(), addDefaultOptions()); + } + + protected Option addPaxExamSpecificOptions() { + return composite(junitBundles(), systemProperty("pax.exam.osgi.unresolved.fail").value("fail")); + } + + protected Option addDefaultOptions() { + return addSlingHome(); + } + + private static Option addDebugOptions() throws IOException { + if (paxRunnerVmOption != null) { + String workDir = FilenameUtils.concat(getBaseDir(), "target/pax"); + File workDirFile = new File(workDir); + if (workDirFile.exists()) { + FileUtils.deleteDirectory(workDirFile); + } + return composite(CoreOptions.vmOption(paxRunnerVmOption), keepCaches(), + systemTimeout(TimeUnit.MINUTES.toMillis(10)), workingDirectory(workDir)); + } + return null; + } + + private static Option addCodeCoverageOption() { + String coverageCommand = System.getProperty(COVERAGE_COMMAND); + if (coverageCommand != null && !coverageCommand.isEmpty()) { + return CoreOptions.vmOption(coverageCommand); + } + return null; + } + + private static Option addSlingHome() { + String workDirName = FilenameUtils.concat(getBaseDir(), "target/sling"); + return systemProperty("sling.home").value(workDirName); + } + + protected static Option configAdmin() { + return mavenBundle("org.apache.felix", "org.apache.felix.configadmin").versionAsInProject(); + } + + protected static Option webSupport() { + return composite(mavenBundle("org.apache.felix", "org.apache.felix.http.bundle").versionAsInProject(), + systemProperty("org.osgi.service.http.port").value(getServerPort())); + } + + protected Option addExtraOptions() { + return new DefaultCompositeOption(); + } + + protected static void delay() { + try { + TimeUnit.MILLISECONDS.sleep(300); + } catch (InterruptedException ie) { + // dont care + } + } + + protected static String getServerPort() { + return System.getProperty(HTTP_PORT_PROP, DEFAULT_PORT); + } +} diff --git a/src/test/java/org/apache/sling/commons/log/webconsole/remote/WebConsoleTestActivator.java b/src/test/java/org/apache/sling/commons/log/webconsole/remote/WebConsoleTestActivator.java new file mode 100644 index 0000000..83686bf --- /dev/null +++ b/src/test/java/org/apache/sling/commons/log/webconsole/remote/WebConsoleTestActivator.java @@ -0,0 +1,131 @@ +/* + * 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.log.webconsole.remote; + +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Dictionary; +import java.util.Hashtable; +import java.util.List; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.turbo.MatchingFilter; +import ch.qos.logback.classic.turbo.TurboFilter; +import ch.qos.logback.core.Appender; +import ch.qos.logback.core.AppenderBase; +import ch.qos.logback.core.filter.Filter; +import ch.qos.logback.core.spi.FilterReply; +import org.apache.sling.commons.log.logback.ConfigProvider; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.slf4j.Marker; +import org.xml.sax.InputSource; + +/** + * Test bundle activator which registers all type of extension point supported by bundle + * Used by ITWebConsoleRemote to assert output of the WebConsole Plugin + */ +public class WebConsoleTestActivator implements BundleActivator { + public static Class[] BUNDLE_CLASS_NAMES = { + WebConsoleTestActivator.class, + WebConsoleTestTurboFilter.class, + WebConsoleTestConfigProvider.class, + WebConsoleTestAppender.class, + WebConsoleTestFilter.class, + }; + + @Override + public void start(BundleContext context) throws Exception { + + context.registerService(TurboFilter.class.getName(),new WebConsoleTestTurboFilter(),null); + context.registerService(ConfigProvider.class.getName(),new WebConsoleTestConfigProvider(),null); + + Dictionary<String, Object> props = new Hashtable<String, Object>(); + String prefix = "WebConsoleTest"; + String[] loggers = { + prefix + ".foo.bar", + prefix + ".foo.baz", + }; + + props.put("loggers", loggers); + context.registerService(Appender.class.getName(), new WebConsoleTestAppender(), props); + + Dictionary<String, Object> props2 = new Hashtable<String, Object>(); + props2.put("appenders", "WebConsoleTestAppender"); + context.registerService(Filter.class.getName(), new WebConsoleTestFilter(), props2); + + String configAsString = "<included> <!-- WebConsoleTestComment --></included>"; + context.registerService(String.class.getName(), configAsString, props); + } + + @Override + public void stop(BundleContext context) throws Exception { + + } + + private static class WebConsoleTestTurboFilter extends MatchingFilter { + @Override + public FilterReply decide(Marker marker, Logger logger, Level level, String format, Object[] params, Throwable t) { + if(logger.getName().equals("turbofilter.foo.bar")){ + return FilterReply.DENY; + } + return FilterReply.NEUTRAL; + } + } + + private static class WebConsoleTestFilter extends Filter<ILoggingEvent> { + + @Override + public FilterReply decide(ILoggingEvent event) { + if(event.getLoggerName().equals("filter.foo.bar")){ + return FilterReply.DENY; + } + return FilterReply.NEUTRAL; + } + } + + private static class WebConsoleTestAppender extends AppenderBase<ILoggingEvent> { + final List<ILoggingEvent> events = new ArrayList<ILoggingEvent>(); + + @Override + protected void append(ILoggingEvent eventObject) { + events.add(eventObject); + } + + @Override + public String getName() { + return "WebConsoleTestAppender"; + } + } + + private static class WebConsoleTestConfigProvider implements ConfigProvider { + + public InputSource getConfigSource() { + String config = "<included> <appender name=\"FILE\" class=\"ch.qos.logback.core.FileAppender\">\n" + + " <file>${sling.home}/logs/webconsoletest1.log</file>\n" + + " <encoder>\n" + + " <pattern>%d %-5level %logger{35} - %msg %n</pattern>\n" + + " </encoder>\n" + + " </appender></included>"; + return new InputSource(new StringReader(config)); + } + } +} diff --git a/src/test/resources/exam.properties b/src/test/resources/exam.properties new file mode 100644 index 0000000..f310420 --- /dev/null +++ b/src/test/resources/exam.properties @@ -0,0 +1,21 @@ +# +# 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. +# + +#Disable the pax logging +pax.exam.logging=none \ No newline at end of file diff --git a/src/test/resources/test-webconsole-remote.xml b/src/test/resources/test-webconsole-remote.xml new file mode 100644 index 0000000..23a2536 --- /dev/null +++ b/src/test/resources/test-webconsole-remote.xml @@ -0,0 +1,39 @@ +<!-- + ~ 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. + --> + +<configuration> + <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> + <encoder> + <pattern>%d{dd.MM.yyyy HH:mm:ss.SSS} *%level* [%thread] %logger %message%n</pattern> + </encoder> + </appender> + + <appender name="FILE" class="ch.qos.logback.core.FileAppender"> + <file>${sling.home}/logs/testremote.log</file> + <encoder> + <pattern>%d %-5level %logger{35} - %msg %n</pattern> + </encoder> + </appender> + + <root level="INFO"> + <appender-ref ref="CONSOLE" /> + <appender-ref ref="FILE" /> + </root> +</configuration> + \ No newline at end of file -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
