You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by bd...@apache.org on 2017/09/06 13:24:25 UTC
svn commit: r1807480 [3/5] - in /sling/whiteboard/microsling: ./ src/
src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/
src/main/java/org/apache/sling/ src/main/java/org/apache/sling/microsling/
src/main/java/org/apache/sling/micros...
Added: sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/helpers/EspReader.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/helpers/EspReader.java?rev=1807480&view=auto
==============================================================================
--- sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/helpers/EspReader.java (added)
+++ sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/helpers/EspReader.java Wed Sep 6 13:24:23 2017
@@ -0,0 +1,758 @@
+/*
+ * 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.microsling.scripting.helpers;
+
+import java.io.FilterReader;
+import java.io.IOException;
+import java.io.PushbackReader;
+import java.io.Reader;
+import java.util.Stack;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The <code>EspReader</code> is a <code>FilterReader</code> which takes
+ * JSP like input and produces plain ECMA script output. The filtering
+ * modifications done on the input comprise the following :
+ * <ul>
+ * <li>Template text (HTML) is wrapped by out.write(). At most one line of
+ * text is wrapped into a single write() call. Double quote characters in the
+ * template text (e.g. for HTML tag attribute values) are escaped.
+ * <li>ECMA code is written to the output as is.
+ * <li>ECMA slash star (/*) comments are also written as is.
+ * <li>ECMA slash slash (//) comments are written as is.
+ * <li>JSP style template comments (<%-- -->) are also removed from the
+ * stream. Lineendings (LFs and CRLFs) are written, though.
+ * <li>HTML comments (<!-- -->) are not treated specially. Rather they are
+ * handled as plain template text written to the output wrapped in
+ * out.write(). The consequence of this behavious is, that as in JSP ECMA
+ * expressions may be included within the comments.
+ * </ul>
+ * <p>
+ * The nice thing about this reader is, that the line numbers of the resulting
+ * stream match the line numbers of the matching contents of the input stream.
+ * Due to the insertion of write() calls, column numbers will not necessarily
+ * match, though. This is especially true if you mix ECMA code tags (<% %>)
+ * with template text on the same line.
+ * <p>
+ * For maximum performance it is advisable to not create the EspReader with a
+ * plain FileReader or InputStreamReader but rather with a BufferedReader based
+ * on one of the simpler Readers. The reasons for this is, that we call the base
+ * reader character by character. This in turn is not too performing if the base
+ * reader does not buffer its input.
+ */
+public class EspReader extends FilterReader {
+
+ /** default log */
+ private final Logger log = LoggerFactory.getLogger(EspReader.class);
+
+ /**
+ * Default parser state. This is the state the parser starts running in. In
+ * this state all text is treated as template text, which should be wrapped
+ * by out.write() line by line.
+ */
+ private static final byte PARSE_STATE_ESP = 1;
+
+ /**
+ * ECMA script reading state. When in this state everything upto to the next
+ * <code>%></code> is written to the output verbatim with three
+ * exceptions : ECMA slash star comments are handed over to handled by the
+ * {@link #PARSE_STATE_ECMA_COMMENT} state, quoted strings are handled in
+ * the {@link #PARSE_STATE_QUOTE} state and ECMA slash slash comments are
+ * handled in {@link #PARSE_STATE_ECMA_COMMENTL} state.
+ */
+ private static final byte PARSE_STATE_ECMA = 3;
+
+ /**
+ * ECMA script expression reading state. This state works exactly the same
+ * as the {@link #PARSE_STATE_ECMA} state with one exception: The whole
+ * code enclosed in the <code><%=</code> ... <code>%></code> tags
+ * is itself wrapped with a <code>out.write()</code> statement
+ * verbatim.
+ */
+ private static final byte PARSE_STATE_ECMA_EXPR = 4;
+
+ /**
+ * JSP comment reading state. When in this state everything upto the closing
+ * <code>--></code> tag is removed from the stream.
+ */
+ private static final byte PARSE_STATE_JSP_COMMENT = 5;
+
+ /**
+ * ECMA quoted string reading state. When in this state everything is
+ * written exactly as in the input stream upto the closing quote, which
+ * matches the opening quote.
+ */
+ private static final byte PARSE_STATE_QUOTE = 6;
+
+ /**
+ * Verbatim copy state. When in this state as many as verbatimChars
+ * characters are returned unchecked. As soon as this number of characters
+ * is returned, the last state is popped from the stack. This state is
+ * mainly used to (re-)inject static text into the output without further
+ * processing.
+ */
+ private static final byte PARSE_STATE_VERBATIM = 8;
+
+ /**
+ * ECMA Comment reading state. When in this state, an ECMA slash star
+ * comment is read (and completely returned).
+ */
+ private static final byte PARSE_STATE_ECMA_COMMENT = 9;
+
+ /**
+ * ECMA Comment reading state. When in this state, an ECMA slash slash
+ * comment is read (and completely returned).
+ */
+ private static final byte PARSE_STATE_ECMA_COMMENTL = 10;
+
+ /**
+ * To work with lookahead and character insertion, we use a PushbackReader.
+ */
+ private PushbackReader input;
+
+ /**
+ * Current parse state. This field contains one of the
+ * <code>PARSE_STATE</code> constants.
+ */
+ private byte state;
+
+ /**
+ * Stack of states. Whenever we enter a new state, the old state is pushed
+ * onto the stack. When a state is left, the previous one is popped from the
+ * stack.
+ *
+ * @see #pushState(byte)
+ * @see #popState()
+ * @see #state
+ */
+ private Stack<Byte> stateStack;
+
+ /**
+ * This value is set to true, if the parser is expected to insert a
+ * out.write() call into the input stream when in state
+ * {@link #PARSE_STATE_ESP}. When this field is true, it is not
+ * necessairily the case, that we are at the start of a real text line.
+ */
+ private boolean lineStart;
+
+ /**
+ * If characters are put into the pushback Stream that should be given back
+ * verbatim, this value is set to the number of such consecutive characters.
+ */
+ private int verbatimChars;
+
+ /**
+ * During String matching this is the character used for string quoting.
+ */
+ private char quoteChar;
+
+ /**
+ * Set to true if an escape character (\) has been encountered within a
+ * quoted string.
+ */
+ private boolean escape;
+
+ /**
+ * Whether the definition of the out variable has already been written or not.
+ * The initial value is <code>true</code> indicating it has still to be
+ * defined.
+ *
+ * @see #startWrite(String)
+ */
+ private boolean outUndefined = true;
+
+ /**
+ * Create an EspReader on top of the given <code>baseReader</code>. The
+ * constructor wraps the input reader with a <code>PushbackReader</code>,
+ * so that input stream modifications may be handled transparently by our
+ * {@link #doRead()} method.
+ */
+ public EspReader(Reader baseReader) {
+ super(baseReader);
+ this.input = new PushbackReader(baseReader, 100);
+ this.stateStack = new Stack<Byte>();
+ this.lineStart = true;
+ this.verbatimChars = -1;
+ this.quoteChar = 0;
+ this.escape = false;
+
+ // Start in ESP (template text) state
+ pushState(PARSE_STATE_ESP);
+ }
+
+ /**
+ * Check whether we may block at the next read() operation. We may be ready
+ * if and only if our input reader is ready. But this does not guarantee
+ * that we won't block, as due to filtering there may be more than one
+ * character needed from the input to return one.
+ *
+ * @return <code>true</code> if a character is available on the
+ * <code>PushbackReader</code>.
+ * @throws IOException if the reader is not open
+ */
+ public boolean ready() throws IOException {
+ ensureOpen();
+ return input.ready();
+ }
+
+ /**
+ * Return the next filtered character. This need not be the next character
+ * of the input stream. It may be a character from the input reader, after
+ * having skipped filtered characters or it may be a character injected due
+ * to translation of template text to ECMA code.
+ *
+ * @return the next character after filtering or -1 at the end of the input
+ * reader
+ * @throws IOException if the reader is not open
+ */
+ public int read() throws IOException {
+ ensureOpen();
+ return doRead();
+ }
+
+ /**
+ * Fill the given buffer with filtered or injected characters. This need not
+ * be the next characters of the input stream. It may be characters from the
+ * input reader, after having skipped filtered characters or it may be a
+ * characters injected due to translation of template text to ECMA code.
+ * This method is exactly the same as
+ * <code>read(cbuf, 0, cbuf.length)</code>.
+ *
+ * @param cbuf The character buffer to fill with (filtered) characters
+ * @return the number of characters filled in the buffer or -1 at the end of
+ * the input reader.
+ * @throws IOException if the reader is not open
+ */
+ public int read(char[] cbuf) throws IOException {
+ return read(cbuf, 0, cbuf.length);
+ }
+
+ /**
+ * Fill the buffer from the offset with the number of characters given. This
+ * need not be the next characters of the input stream. It may be characters
+ * from the input reader, after having skipped filtered characters or it may
+ * be a characters injected due to translation of template text to ECMA
+ * code.
+ *
+ * @param cbuf The character buffer to fill with (filtered) characters
+ * @param off Offset from where to start in the buffer
+ * @param len The number of characters to fill into the buffer
+ * @return the number of characters filled in the buffer or -1 at the end of
+ * the input reader.
+ * @throws IOException if the reader is not open
+ * @throws IndexOutOfBoundsException if len is negative, off is negative or
+ * higher than the buffer length or off+len is negative or
+ * beyond the buffer size.
+ */
+ public int read(char[] cbuf, int off, int len) throws java.io.IOException {
+ ensureOpen();
+
+ // Check lines (taken from InputStreamReader ;-)
+ if ((off < 0) || (off > cbuf.length) || (len < 0)
+ || ((off + len) > cbuf.length) || ((off + len) < 0)) {
+ throw new IndexOutOfBoundsException();
+ } else if (len == 0) {
+ return 0;
+ }
+
+ int i;
+ for (i = 0; i < len; i++, off++) {
+ int c = doRead();
+ if (c < 0) {
+ break;
+ }
+ cbuf[off] = (char) c;
+ }
+
+ // return EOF (-1) if none have been read, else return the number read
+ return (i == 0) ? -1 : i;
+ }
+
+ /**
+ * Skip the number of filtered characters. The skip method is the same as
+ * calling read() repeatedly for the given number of characters and throwing
+ * away the result. If the end of input reader is reached before having
+ * skipped the number of characters, the method returns the number
+ * characters skipped so far.
+ *
+ * @param n the number of (filtered) characters to skip
+ * @return the number of (filtered) characters actually skipped
+ * @throws IllegalArgumentException if n is negative
+ * @throws IOException if the reading the characters throws
+ */
+ public long skip(long n) throws IOException {
+ if (n < 0L) {
+ throw new IllegalArgumentException("skip value is negative");
+ }
+
+ long i = -1;
+ while (++i < n) {
+ if (doRead() < 0) {
+ break;
+ }
+ }
+ return i;
+ }
+
+ /**
+ * Close the EspReader.
+ */
+ public void close() throws java.io.IOException {
+ if (input != null) {
+ input.close();
+ input = null;
+ }
+
+ // I dont' know what happens ??
+ super.close();
+ }
+
+ /**
+ * Mark the present position in the stream. The <code>mark</code> for
+ * class <code>EspReader</code> always throws an throwable.
+ *
+ * @param readAheadLimit The number of characters to read ahead
+ * @exception IOException Always, since mark is not supported
+ */
+ public void mark(int readAheadLimit) throws IOException {
+ throw new IOException("mark() not supported");
+ }
+
+ /**
+ * Tell whether this stream supports the mark() operation, which it does
+ * not.
+ *
+ * @return false Always, since mark is not supported
+ */
+ public boolean markSupported() {
+ return false;
+ }
+
+ /**
+ * Reset the stream. The <code>reset</code> method of
+ * <code>EspReader</code> always throws an throwable.
+ *
+ * @exception IOException Always, since reset is not supported
+ */
+ public void reset() throws IOException {
+ throw new IOException("reset() not supported");
+ }
+
+ /**
+ * Internal routine doing all the footwork of reading one character at a
+ * time from the <code>PushbackReader</code> and acting according to the
+ * current state.
+ * <p>
+ * This filter is implemented using a finite state machine using the states
+ * defined above with the <code>PARSE_STATE</code> constants. Each state
+ * may do a look ahead in certain situations to decide on further steps.
+ * Characters looked ahead may or may not be inserted back into the input
+ * stream depending on the concrete state.
+ *
+ * @return the next character from the input stream according to the current
+ * state or -1 to indicate end of file.
+ * @throws IOException if the input <code>PushbackReader</code> throws it
+ */
+ private int doRead() throws IOException {
+
+ // we return out of the loop, if we find a character passing the filter
+ for (;;) {
+
+ // Get a character from the input, which may well have been
+ // injected using the unread() method
+ int c = input.read();
+
+ // catch EOF
+ if (c < 0) {
+
+ // if a template text line is still incomplete, inject
+ // proper line ending and continue until this has been returned
+ if (!lineStart && state == PARSE_STATE_ESP) {
+ doVerbatim("\");"); // line ending injection
+ lineStart = true; // mark the line having ended
+ continue; // let's start read the injection
+ }
+
+ return c; // return the marker, we're done
+ }
+
+ // Do the finite state machine
+ switch (state) {
+
+ // NOTE :
+ // - continue means ignore current character, read next
+ // - break means return current character
+
+ // Template text state - text is wrapped in out.write()
+ case PARSE_STATE_ESP:
+
+ // might start ECMA code/expr, ESP comment or JSP comment
+ if (c == '<') {
+ int c2 = input.read();
+ int c3 = input.read();
+
+ if (c2 == '%') {
+
+ // ECMA or JSP comment
+
+ if (c3 == '=') {
+
+ // ECMA expression <%= ... %>
+ pushState(PARSE_STATE_ECMA_EXPR);
+ startWrite(null);
+ if (!lineStart) {
+ doVerbatim("\");");
+ }
+ continue;
+
+ } else if (c3 == '-') {
+
+ // (Possible) JSP Comment <%-- ... --%>
+ int c4 = input.read();
+ if (c4 == '-') {
+ pushState(PARSE_STATE_JSP_COMMENT);
+ continue;
+ }
+ input.unread(c4);
+
+ }
+
+ // We only get here if we are sure about ECMA
+
+ // ECMA code <% ... %>
+ input.unread(c3);
+ pushState(PARSE_STATE_ECMA);
+ if (!lineStart) {
+ doVerbatim("\");");
+ }
+ continue;
+
+ }
+
+ // Nothing special, push back read ahead
+ input.unread(c3);
+ input.unread(c2);
+
+ // End of template text line
+ } else if (c == '\r' || c == '\n') {
+ String lineEnd; // will be injected
+
+ // Check for real CRLF
+ if (c == '\r') {
+ int c2 = input.read();
+ if (c2 != '\n') {
+ input.unread(c2);
+ lineEnd = "\\r";
+ } else {
+ lineEnd = "\\r\\n";
+ }
+ } else {
+ lineEnd = "\\n";
+ }
+
+ // Only write line ending if not empty
+ if (!lineStart) {
+ doVerbatim("\");\n");
+ doVerbatim(lineEnd);
+ lineStart = true;
+
+ } else { // if (lineEnd.length() > 1) {
+ // no matter what line ending we have, make it LF
+ doVerbatim("\");\n");
+ doVerbatim(lineEnd);
+ startWrite("\"");
+ }
+
+ continue;
+
+ // template text is wrapped with double quotes, which
+ // when occurring in the text must be escaped.
+ // We also escape the escape character..
+ } else if (c == '"' || c == '\\') {
+
+ doVerbatim(String.valueOf((char) c));
+ c = '\\';
+
+ }
+
+ // If in template text at the beginning of a line
+ if (lineStart) {
+ lineStart = false;
+ startWrite("\"" + (char) c);
+ continue;
+ }
+
+ break;
+
+ // Reading ECMA code or and ECMA expression
+ case PARSE_STATE_ECMA_EXPR:
+ case PARSE_STATE_ECMA:
+
+ if (c == '%') {
+
+ // might return to PARSE_STATE_ESP
+ int c2 = input.read();
+ if (c2 == '>') {
+
+ // An expression is wrapped in out.write()
+ if (popState() == PARSE_STATE_ECMA_EXPR) {
+ doVerbatim(");");
+ }
+
+ // next ESP needs out.write(
+ lineStart = true;
+
+ continue;
+
+ }
+
+ // false alert, push back
+ input.unread(c2);
+
+ } else if (c == '/') {
+
+ // might be ECMA Comment
+ int c2 = input.read();
+ if (c2 == '/') {
+ // single line comment
+ pushState(PARSE_STATE_ECMA_COMMENTL);
+ } else if (c2 == '*') {
+ // multiline comment
+ pushState(PARSE_STATE_ECMA_COMMENT);
+ }
+
+ // false alert, push back
+ input.unread(c2);
+
+ } else if (c == '\'' || c == '"') {
+
+ // an ECMA string
+ escape = false; // start unescaped
+ quoteChar = (char) c; // to recognize the end
+ pushState(PARSE_STATE_QUOTE);
+
+ }
+ break;
+
+ // Reading a JSP comment, only returning line endings
+ case PARSE_STATE_JSP_COMMENT:
+
+ // JSP comments end complexly with --%>
+ if (c == '-') {
+ int c2 = input.read();
+ if (c2 == '-') {
+ int c3 = input.read();
+ if (c3 == '%') {
+ int c4 = input.read();
+ if (c4 == '>') {
+
+ // we really reached the end ...
+ popState();
+ continue;
+
+ }
+ input.unread(c4);
+ }
+ input.unread(c3);
+ }
+ input.unread(c2);
+
+ // well, not definitely correct but reasonably accurate
+ // ;-)
+ } else if (c == '\r' || c == '\n') {
+
+ // terminate an open template line
+ if (!lineStart) {
+ input.unread(c); // push back the character
+ doVerbatim("\");"); // insert ");
+ lineStart = true; // mark the line start
+ continue; // Force read of the "
+ }
+
+ break;
+ }
+
+ // continue reading another character in the comment
+ continue;
+
+ // Read an ECMA string upto the ending quote character
+ case PARSE_STATE_QUOTE:
+
+ // if unescaped quote character
+ if (c == quoteChar && !escape) {
+ popState();
+ } else {
+ // mark escape - only if not already escaped (bug 7079)
+ escape = c == '\\' && !escape;
+ }
+
+ break;
+
+ // Return characters unfiltered
+ case PARSE_STATE_VERBATIM:
+
+ // Go back to previous state if all characters read
+ if (--verbatimChars < 0) {
+ popState();
+ }
+
+ break;
+
+ // Return an ECMA multiline comment, ending with */
+ case PARSE_STATE_ECMA_COMMENT:
+
+ // Might be the end of the comment
+ if (c == '*') {
+ int c2 = input.read();
+ if (c2 == '/') {
+ popState(); // back to previous
+ doVerbatim("/"); // return slash verbatim
+ } else {
+ input.unread(c2);
+ }
+ }
+
+ break;
+
+ // Return an ECMA single line comment, ending with end of line
+ case PARSE_STATE_ECMA_COMMENTL:
+
+ // CRLF recognition
+ if (c == '\r') {
+ int c2 = input.read();
+ if (c2 == '\n') {
+ popState();
+ }
+ input.unread(c2);
+
+ // LF only line end
+ } else if (c == '\n') {
+ popState();
+ }
+
+ break;
+
+ // What ???!!!
+ default:
+
+ // we warn and go back to default state
+ log.warn("doRead(): unknown state " + state);
+ state = PARSE_STATE_ESP;
+
+ break;
+
+ } // switch
+
+ // Exiting the switch normally we return the current character
+ return c;
+
+ } // for(;;)
+
+ }
+
+ /**
+ * Throw an IOException if the reader is not open
+ *
+ * @throws IOException if the reader is (already) closed
+ */
+ private void ensureOpen() throws IOException {
+ if (input == null) {
+ throw new IOException("Reader is closed");
+ }
+ }
+
+ /**
+ * Injects the call to write template text and checks whether the global
+ * <em>out</em> variable has also to be defined such that the writer is
+ * acquired on demand.
+ *
+ * @param startString Additional data to be injected as initial argument
+ * to the <em>out.write</em> call written. If <code>null</code> just
+ * the method call is injected.
+ *
+ * @throws IOException if the 'unreading' throws
+ */
+ private void startWrite(String startString) throws IOException {
+
+ // inject the out.write( part and the initial string
+ if (startString != null && startString.length() > 0) {
+ doVerbatim(startString);
+ }
+ doVerbatim("out.write(");
+
+ // if out is not set yet, we also acquire it now setting it
+ // globally
+ if (outUndefined) {
+ doVerbatim("out=response.writer;");
+ outUndefined = false;
+ }
+ }
+
+ /**
+ * Injects a string into the input stream, sets the number of characters to
+ * return verbatim and change state. The state change only happens if we are
+ * not in verbatim state already. Else the current string is simply
+ * prepended to the previous inhjection. This is simply a convenience method
+ * ;-)
+ *
+ * @param verbatimString The string to inject into the input stream
+ * @throws IOException if the 'unreading' throws
+ */
+ private void doVerbatim(String verbatimString) throws IOException {
+
+ // Push 'back' into PushbackReader
+ input.unread(verbatimString.toCharArray());
+
+ // Set the number of characters to return verbatim
+ verbatimChars += verbatimString.length();
+
+ // Change state if not already in verbatim state
+ if (state != PARSE_STATE_VERBATIM) {
+ pushState(PARSE_STATE_VERBATIM);
+ }
+ }
+
+ /**
+ * Push the current state on stack and set to <code>newState</code>. This
+ * new state is also returned.
+ *
+ * @param newState the new state to set
+ * @return the new state set according to <code>newState</code>
+ */
+ private byte pushState(byte newState) {
+ stateStack.push(state);
+ return state = newState;
+ }
+
+ /**
+ * Sets the current state to the state stored at the top of the stack. If
+ * the stack is empty prior to this call, the default template text state is
+ * set. The method returns the state prior to setting to the new state.
+ *
+ * @return the state prior to calling this method
+ */
+ private byte popState() {
+ byte oldState = state;
+ state = stateStack.isEmpty() ? PARSE_STATE_ESP : stateStack.pop();
+ return oldState;
+ }
+
+}
\ No newline at end of file
Added: sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/helpers/FileNodeRequestAttribute.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/helpers/FileNodeRequestAttribute.java?rev=1807480&view=auto
==============================================================================
--- sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/helpers/FileNodeRequestAttribute.java (added)
+++ sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/helpers/FileNodeRequestAttribute.java Wed Sep 6 13:24:23 2017
@@ -0,0 +1,71 @@
+/*
+ * 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.microsling.scripting.helpers;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.sling.microsling.helpers.exceptions.MissingRequestAttributeException;
+
+/** Stores an nt:file Node in a request attribute,
+ * and allows the file's inputStream or reader to
+ * be accessed easily.
+ * Used by scripting SlingServlets to store resolved script nodes
+ * in between canProcess and doX method calls.
+ */
+public class FileNodeRequestAttribute {
+
+ private final Node node;
+ public static final String REQ_ATTR_NAME = FileNodeRequestAttribute.class.getName();
+
+ /** Store this as an attribute of req */
+ public FileNodeRequestAttribute(Node n,HttpServletRequest req) {
+ node = n;
+ req.setAttribute(REQ_ATTR_NAME,this);
+ }
+
+ /** Retrieve a FileNodeRequestAttribute from given request */
+ public static FileNodeRequestAttribute getFromRequest(HttpServletRequest req) throws MissingRequestAttributeException {
+ final FileNodeRequestAttribute result =
+ (FileNodeRequestAttribute)req.getAttribute(REQ_ATTR_NAME);
+ if(result==null) {
+ throw new MissingRequestAttributeException(REQ_ATTR_NAME);
+ }
+ return result;
+ }
+
+ /** Return our nt:file node */
+ public Node getNode() {
+ return node;
+ }
+
+ /** Return an InputStream that provides our node's file content */
+ public InputStream getInputStream() throws RepositoryException {
+ // TODO need more robust checks
+ return node.getNode("jcr:content").getProperty("jcr:data").getStream();
+ }
+
+ /** Return a Reader that provides our node's file content */
+ public Reader getReader() throws RepositoryException {
+ return new InputStreamReader(getInputStream());
+ }
+}
Added: sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/helpers/ScriptFilenameBuilder.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/helpers/ScriptFilenameBuilder.java?rev=1807480&view=auto
==============================================================================
--- sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/helpers/ScriptFilenameBuilder.java (added)
+++ sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/helpers/ScriptFilenameBuilder.java Wed Sep 6 13:24:23 2017
@@ -0,0 +1,82 @@
+/*
+ * 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.microsling.scripting.helpers;
+
+import org.apache.sling.microsling.helpers.constants.HttpConstants;
+
+/** Builds the names of script files based on the current input:
+ * <ul>
+ * <li>HTTP request method name</li>
+ * <li>Request selectors, if any</li>
+ * <li>Desired response Content-Type</li>
+ * <li>Desired script extension</li>
+ * </ul>
+ *
+ * See ScriptFilenameBuilderTest for examples.
+ *
+ * Note that names can include partial paths, for example we return
+ * "print/a4/html.js" for a GET request for an html document with
+ * selectors "print.a4".
+ */
+public class ScriptFilenameBuilder {
+
+ /** @return a name like "html.js" or "print/a4/html.vlt" or "POST.js" */
+ public String buildScriptFilename(String methodName,String selectors,String contentType,String scriptExtension) {
+ final StringBuffer sb = new StringBuffer();
+
+ // path before filename:
+ // add selectors in front of the filename if any, replacing dots in them by slashes
+ // so that print.a4 becomes print/a4/
+ if(selectors != null && selectors.length() > 0) {
+ sb.append(selectors.toLowerCase().replace('.','/'));
+ sb.append('/');
+ }
+
+ // filename:
+ if(methodName==null || methodName.length() == 0) {
+ sb.append("NO_METHOD");
+
+ } else if(HttpConstants.METHOD_GET.equalsIgnoreCase(methodName)) {
+ // for the GET method, use the simplified content-type, lowercased,
+ // as the filename.
+ // TODO: how to handle HEAD?
+ if(contentType == null || contentType.length() == 0) {
+ sb.append("NO_CONTENT_TYPE");
+ } else {
+ // keep only what follows slash in the content-type
+ final String [] splitContentType = contentType.split("/");
+ sb.append(splitContentType[splitContentType.length - 1].toLowerCase());
+ }
+
+ } else {
+ // for other methods use the method name
+ sb.append(methodName.toUpperCase());
+ }
+
+ // extension: use desired script extension
+ sb.append(".");
+ if(scriptExtension == null || scriptExtension.length()==0) {
+ sb.append("NO_EXT");
+ } else {
+ sb.append(scriptExtension.toLowerCase());
+ }
+ return sb.toString();
+ }
+
+}
Added: sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/helpers/ScriptHelper.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/helpers/ScriptHelper.java?rev=1807480&view=auto
==============================================================================
--- sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/helpers/ScriptHelper.java (added)
+++ sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/helpers/ScriptHelper.java Wed Sep 6 13:24:23 2017
@@ -0,0 +1,159 @@
+/*
+ * $Url: $
+ * $Id: $
+ *
+ * Copyright 1997-2005 Day Management AG
+ * Barfuesserplatz 6, 4001 Basel, Switzerland
+ * All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Day Management AG, ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Day.
+ */
+package org.apache.sling.microsling.scripting.helpers;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Writer;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+/** Simple script helper providing access to the (wrapped) response, the
+ * on-demand writer and a simple API for request inclusion. Instances of this
+ * class are made available to the scripts as the global <code>sling</code>
+ * variable.
+ */
+public class ScriptHelper {
+
+ private final HttpServletRequest request;
+
+ private final HttpServletResponse response;
+
+ public ScriptHelper(HttpServletRequest request, HttpServletResponse response) {
+ this.request = request;
+ this.response = new OnDemandWriterResponse(response);
+ }
+
+ public HttpServletRequest getRequest() {
+ return request;
+ }
+
+ public HttpServletResponse getResponse() {
+ return response;
+ }
+
+ public void include(String path) throws ServletException, IOException {
+ RequestDispatcher dispatcher = getRequest().getRequestDispatcher(path);
+ if (dispatcher != null) {
+ dispatcher.include(getRequest(), getResponse());
+ }
+ }
+
+ /** Simple Response wrapper returning an on-demand writer when asked for
+ * a writer.
+ */
+ private static class OnDemandWriterResponse extends HttpServletResponseWrapper {
+
+ private PrintWriter writer;
+
+ OnDemandWriterResponse(HttpServletResponse delegatee) {
+ super(delegatee);
+ }
+
+ @Override
+ public PrintWriter getWriter() {
+ if (writer == null) {
+ writer = new PrintWriter(new OnDemandWriter(getResponse()));
+ }
+
+ return writer;
+ }
+ }
+
+ /** A writer acquiring the actual writer to delegate to on demand when the
+ * first data is to be written. */
+ private static class OnDemandWriter extends Writer {
+
+ private final ServletResponse response;
+ private Writer delegatee;
+
+ OnDemandWriter(ServletResponse response) {
+ this.response = response;
+ }
+
+ private Writer getWriter() throws IOException {
+ if (delegatee == null) {
+ delegatee = response.getWriter();
+ }
+
+ return delegatee;
+ }
+
+ @Override
+ public void write(int c) throws IOException {
+ synchronized (lock) {
+ getWriter().write(c);
+ }
+ }
+
+ @Override
+ public void write(char[] cbuf) throws IOException {
+ synchronized (lock) {
+ getWriter().write(cbuf);
+ }
+ }
+
+ @Override
+ public void write(char[] cbuf, int off, int len) throws IOException {
+ synchronized (lock) {
+ getWriter().write(cbuf, off, len);
+ }
+ }
+
+ @Override
+ public void write(String str) throws IOException {
+ synchronized (lock) {
+ getWriter().write(str);
+ }
+ }
+
+ @Override
+ public void write(String str, int off, int len) throws IOException {
+ synchronized (lock) {
+ getWriter().write(str, off, len);
+ }
+ }
+
+ @Override
+ public void flush() throws IOException {
+ synchronized (lock) {
+ Writer writer = delegatee;
+ if (writer != null) {
+ writer.flush();
+ }
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ synchronized (lock) {
+ // flush and close the delegatee if existing, otherwise ignore
+ Writer writer = delegatee;
+ if (writer != null) {
+ writer.flush();
+ writer.close();
+
+ // drop the delegatee now
+ delegatee = null;
+ }
+ }
+ }
+ }
+}
Added: sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/services/MicroslingServiceLocator.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/services/MicroslingServiceLocator.java?rev=1807480&view=auto
==============================================================================
--- sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/services/MicroslingServiceLocator.java (added)
+++ sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/services/MicroslingServiceLocator.java Wed Sep 6 13:24:23 2017
@@ -0,0 +1,59 @@
+/*
+ * 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.microsling.services;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.sling.microsling.api.ResourceResolver;
+import org.apache.sling.microsling.api.ServiceLocator;
+import org.apache.sling.microsling.resource.MicroslingResourceResolver;
+
+/** Poor man's ServiceLocator (no, poorer than that) which uses a
+ * static list of services. This is mostly meant to introduce
+ * the ServiceLocator interface in microsling.
+ * See Sling OSGi for the real McCoy.
+ */
+
+public class MicroslingServiceLocator implements ServiceLocator {
+
+ protected final Map <String,Object> services = new HashMap <String, Object> ();
+
+ public MicroslingServiceLocator() {
+ // initialize our services (pure rocket science, isn't it? ;-)
+ services.put(ResourceResolver.class.getName(),new MicroslingResourceResolver());
+ }
+
+ public Object getService(String serviceName) {
+ return services.get(serviceName);
+ }
+
+
+ public Object getRequiredService(String serviceName) throws ServiceNotAvailableException {
+ final Object result = services.get(serviceName);
+ if(result== null) {
+ throw new ServiceNotAvailableException("Service '" + serviceName + "' is not available");
+ }
+ return result;
+ }
+
+ public Object[] getServices(String serviceName, String filter) {
+ // we don't have this feature
+ return null;
+ }
+
+}
Added: sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/servlet/MicroSlingFilterHelper.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/servlet/MicroSlingFilterHelper.java?rev=1807480&view=auto
==============================================================================
--- sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/servlet/MicroSlingFilterHelper.java (added)
+++ sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/servlet/MicroSlingFilterHelper.java Wed Sep 6 13:24:23 2017
@@ -0,0 +1,169 @@
+/*
+ * 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.microsling.servlet;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.sling.microsling.servlet.MicroslingMainServlet;
+
+/** This is helper code, not very interesting to study (but it's
+ * not in an "helpers" package as that would require too much public
+ * stuff).
+ *
+ * Manages the microsling chain of servlet Filters:
+ * stores the list, calls them when processing a request
+ * and calls microSlingServlet.doService after that.
+ */
+class MicroSlingFilterHelper {
+
+ private MicroslingMainServlet microSling;
+
+ private List<Filter> requestFilterList = new LinkedList<Filter>();
+
+ private Filter[] requestFilters;
+
+ MicroSlingFilterHelper(MicroslingMainServlet microSling) {
+ this.microSling = microSling;
+ }
+
+ void destroy() {
+ Filter[] filters = getFilters();
+
+ // clean up
+ requestFilterList.clear();
+ requestFilters = null;
+
+ // destroy the filters
+ for (int i=0; i < filters.length; i++) {
+ try {
+ filters[i].destroy();
+ } catch (Throwable t) {
+ // TODO: some logging would probably be usefull
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.FilterChain#doFilter(javax.servlet.ServletRequest,
+ * javax.servlet.ServletResponse)
+ */
+ void service(ServletRequest request, ServletResponse response)
+ throws IOException, ServletException {
+
+ MicroSlingFilterChain filterChain = new MicroSlingFilterChain(
+ microSling, getFilters());
+ filterChain.doFilter(request, response);
+
+ }
+
+ /** return our Filters as a (lazily created) array */
+ private Filter[] getFilters() {
+ if (requestFilters == null) {
+ requestFilters = requestFilterList.toArray(new Filter[requestFilterList.size()]);
+ }
+ return requestFilters;
+ }
+
+ /** Add a Filter at the end of our current chain */
+ void addFilter(final Filter filter) throws ServletException {
+ FilterConfig config = new FilterConfig() {
+ public String getFilterName() {
+ return filter.getClass().getName();
+ }
+
+ public String getInitParameter(String arg0) {
+ // no parameters for now
+ return null;
+ }
+
+ public Enumeration<?> getInitParameterNames() {
+ // no parameters for now
+ return Collections.enumeration(Collections.emptyList());
+ }
+
+ public ServletContext getServletContext() {
+ return microSling.getServletContext();
+ }
+ };
+
+ // initialize the filter and add it to the list
+ filter.init(config);
+ requestFilterList.add(filter);
+
+ // force recreation of filter list
+ requestFilters = null;
+ }
+
+ /** A FilterChain that applies all Filters in an array and calls
+ * MicroSlingServlet.doFilter when done
+ */
+ private static class MicroSlingFilterChain implements FilterChain {
+
+ private final MicroslingMainServlet microSlingServlet;
+
+ private final Filter[] requestFilters;
+
+ private int currentFilter;
+
+ private MicroSlingFilterChain(MicroslingMainServlet microSlingServlet,
+ Filter[] requestFilters) {
+ this.microSlingServlet = microSlingServlet;
+ this.requestFilters = requestFilters;
+ this.currentFilter = -1;
+ }
+
+ public void doFilter(ServletRequest request, ServletResponse response)
+ throws IOException, ServletException {
+
+ currentFilter++;
+
+ if (currentFilter < requestFilters.length) {
+ // call the next filter
+ requestFilters[currentFilter].doFilter(request, response, this);
+
+ } else {
+ // done with filters, call microsling servlet
+ HttpServletRequest hReq = null;
+ HttpServletResponse hRep = null;
+ try {
+ hReq = (HttpServletRequest)request;
+ hRep = (HttpServletResponse)response;
+ } catch(ClassCastException cce) {
+ throw new ServletException("Expected an HttpServletRequest and HttpServletResponse",cce);
+ }
+ microSlingServlet.doService(hReq,hRep);
+ }
+
+ }
+ }
+}
Added: sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/servlet/MicroslingMainServlet.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/servlet/MicroslingMainServlet.java?rev=1807480&view=auto
==============================================================================
--- sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/servlet/MicroslingMainServlet.java (added)
+++ sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/servlet/MicroslingMainServlet.java Wed Sep 6 13:24:23 2017
@@ -0,0 +1,238 @@
+/*
+ * 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.microsling.servlet;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.Filter;
+import javax.servlet.GenericServlet;
+import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.sling.microsling.api.Resource;
+import org.apache.sling.microsling.api.ServiceLocator;
+import org.apache.sling.microsling.api.SlingRequestContext;
+import org.apache.sling.microsling.api.exceptions.HttpStatusCodeException;
+import org.apache.sling.microsling.api.exceptions.SlingException;
+import org.apache.sling.microsling.contenttype.ResponseContentTypeResolverFilter;
+import org.apache.sling.microsling.request.MicroslingRequestContext;
+import org.apache.sling.microsling.scripting.SlingScriptResolver;
+import org.apache.sling.microsling.services.MicroslingServiceLocator;
+import org.apache.sling.microsling.slingservlets.DefaultSlingServlet;
+import org.apache.sling.microsling.slingservlets.StreamServlet;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The main microsling servlet: apply Filters to the request using our
+ * MicroSlingFilterHelper, select and delegate to a SlingServlet to process the
+ * request.
+ */
+public class MicroslingMainServlet extends GenericServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ private MicroSlingFilterHelper filterChain;
+
+ private ServiceLocator serviceLocator;
+
+ private Map<String, Servlet> servlets;
+
+ private SlingScriptResolver scriptResolver;
+
+ private DefaultSlingServlet defaultSlingServlet;
+
+ private static final Logger log = LoggerFactory.getLogger(MicroslingMainServlet.class);
+
+ @Override
+ public void init() throws ServletException {
+ super.init();
+ servlets = new HashMap<String, Servlet>();
+ initFilterChain();
+ initServlets();
+ initServiceLocator();
+ initScriptResolver();
+ }
+
+ /** init our filter chain */
+ protected void initFilterChain() throws ServletException {
+ filterChain = new MicroSlingFilterHelper(this);
+ addFilter(new ResponseContentTypeResolverFilter());
+ }
+
+ /** init our servlets */
+ protected void initServlets() throws ServletException {
+ // TODO use a utility class to map nt:file to the magic NODETYPES path
+ addServlet("NODETYPES/nt/file", new StreamServlet());
+ defaultSlingServlet = new DefaultSlingServlet();
+ }
+
+ /** init our serviceLocator */
+ protected void initServiceLocator() throws ServletException {
+ serviceLocator = new MicroslingServiceLocator();
+ }
+
+ /** init our scriptResolver */
+ protected void initScriptResolver() throws ServletException {
+ scriptResolver = new SlingScriptResolver();
+ }
+
+ /**
+ * Execute our Filters via MicroSlingFilterHelper, which calls our doService
+ * method after executing the filters
+ */
+ public void service(ServletRequest req, ServletResponse resp)
+ throws ServletException, IOException {
+
+ // our filters might need the SlingRequestContext to store info in it
+ new MicroslingRequestContext(getServletContext(), req, resp, serviceLocator);
+ filterChain.service(req, resp);
+ }
+
+ @Override
+ public void destroy() {
+ // just for completeness, we have to take down our filters
+ if (filterChain != null) {
+ filterChain.destroy();
+ filterChain = null;
+ }
+
+ // destroy registered servlets
+ Servlet[] servletList = servlets.values().toArray(
+ new Servlet[servlets.size()]);
+ for (Servlet servlet : servletList) {
+ try {
+ servlet.destroy();
+ } catch (Throwable t) {
+ getServletContext().log(
+ "Unexpected problem destroying servlet " + servlet, t);
+ }
+ }
+ servlets.clear();
+
+ // destroy base class at the end
+ super.destroy();
+ }
+
+ /**
+ * Called by
+ * {@link MicroSlingFilterHelper#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse)}
+ * after all filters have been processed.
+ */
+ void doService(HttpServletRequest req, HttpServletResponse resp)
+ throws IOException {
+
+ try {
+ // Select a SlingServlet and delegate the actual request processing
+ // to it
+ final Servlet selectedServlet = selectSlingServlet(req);
+ if (selectedServlet != null) {
+ delegateToSlingServlet(selectedServlet, req, resp);
+ } else {
+ // no typed servlet, so lets try scripting
+ boolean scriptExists = scriptResolver.evaluateScript(req, resp);
+ if (!scriptExists) {
+ if(log.isDebugEnabled()) {
+ final SlingRequestContext ctx = SlingRequestContext.getFromRequest(req);
+ final Resource r = ctx.getResource();
+ log.debug("No specific Servlet or script found for Resource " + r + ", using default Servlet");
+ }
+ delegateToSlingServlet(defaultSlingServlet, req, resp);
+ }
+ }
+
+ } catch (HttpStatusCodeException hts) {
+ resp.sendError(hts.getStatusCode(), hts.getMessage());
+
+ } catch (Exception e) {
+ final StringWriter sw = new StringWriter();
+ e.printStackTrace(new PrintWriter(sw, true));
+ resp.sendError(500, e.getMessage() + "\n" + sw.toString());
+ }
+ }
+
+ /** Select a SlingServlet to process the given request */
+ protected Servlet selectSlingServlet(HttpServletRequest req)
+ throws SlingException {
+
+ // use the resource type to select a servlet
+ final SlingRequestContext ctx = SlingRequestContext.getFromRequest(req);
+ final Resource r = ctx.getResource();
+ String type = (r == null ? null : r.getResourceType());
+ final Servlet result = (type != null) ? servlets.get(type) : null;
+
+ if(log.isDebugEnabled()) {
+ if(result==null) {
+ log.debug("No Servlet found for resource type " + type);
+ } else {
+ log.debug("Using Servlet class " + result.getClass().getSimpleName() + " for resource type " + type);
+ }
+ }
+
+ return result;
+ }
+
+ /** Delegate to the given SlingServlet, based on the request HTTP method */
+ protected void delegateToSlingServlet(Servlet s, HttpServletRequest req,
+ HttpServletResponse resp) throws ServletException, IOException {
+ s.service(req, resp);
+ }
+
+ /** Add a filter to our MicroSlingFilterHelper */
+ protected void addFilter(Filter filter) throws ServletException {
+ filterChain.addFilter(filter);
+ }
+
+ /** Add servlets by resource type */
+ protected void addServlet(final String resourceType, Servlet servlet) {
+
+ try {
+ ServletConfig config = new ServletConfig() {
+ public String getInitParameter(String name) {
+ return MicroslingMainServlet.this.getInitParameter(name);
+ }
+ public Enumeration<?> getInitParameterNames() {
+ return MicroslingMainServlet.this.getInitParameterNames();
+ }
+ public ServletContext getServletContext() {
+ return MicroslingMainServlet.this.getServletContext();
+ }
+ public String getServletName() {
+ return resourceType;
+ }
+ };
+ servlet.init(config);
+
+ // only register if initialization succeeds
+ servlets.put(resourceType, servlet);
+ } catch (Throwable t) {
+ getServletContext().log("Failed initializing servlet " + servlet + " for type " + resourceType, t);
+ }
+
+ }
+}
Added: sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/slingservlets/DefaultSlingServlet.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/slingservlets/DefaultSlingServlet.java?rev=1807480&view=auto
==============================================================================
--- sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/slingservlets/DefaultSlingServlet.java (added)
+++ sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/slingservlets/DefaultSlingServlet.java Wed Sep 6 13:24:23 2017
@@ -0,0 +1,204 @@
+/*
+ * 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.microsling.slingservlets;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Enumeration;
+
+import javax.jcr.Item;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.sling.microsling.api.Resource;
+import org.apache.sling.microsling.api.SlingRequestContext;
+import org.apache.sling.microsling.api.exceptions.HttpStatusCodeException;
+import org.apache.sling.microsling.helpers.servlets.SlingAllMethodsServlet;
+
+/**
+ * The default SlingServlet, used if no other SlingServlet wants to process the
+ * current request.
+ */
+public class DefaultSlingServlet extends SlingAllMethodsServlet {
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ resp.setContentType("text/plain");
+
+ final SlingRequestContext ctx = SlingRequestContext.getFromRequest(req);
+ final Resource r = ctx.getResource();
+ if (r == null) {
+ throw new HttpStatusCodeException(404, "Resource not found: "
+ + req.getPathInfo());
+ }
+
+ final Item data = r.getItem();
+ if (data != null) {
+ final PrintWriter pw = resp.getWriter();
+ try {
+ if (data.isNode()) {
+ dump(pw, r, (Node) data);
+ } else {
+ dump(pw, r, (Property) data);
+ }
+ } catch (RepositoryException re) {
+ throw new ServletException("Cannot dump contents of "
+ + ctx.getResource().getURI(), re);
+ }
+ } else {
+ throw new HttpStatusCodeException(501,
+ "Not implemented: resource " + ctx.getResource().getURI()
+ + " cannot be dumped by " + getClass().getSimpleName());
+ }
+ }
+
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ final SlingRequestContext ctx = SlingRequestContext.getFromRequest(req);
+ String redirectPath = req.getPathInfo();
+
+ Session s = null;
+ try {
+ s = ctx.getSession();
+ Node current = (ctx.getResource() == null ? null : (Node)ctx.getResource().getItem());
+
+ // Decide whether to create or update a node
+ // TODO: this is a simplistic way of deciding, for now: if we have
+ // no Resource or if the Node that it points to already has child nodes,
+ // we create a new node. Else we update the current node.
+ if(current == null || current.hasNodes()) {
+ final String parentPath = (current == null ? req.getPathInfo() : current.getPath());
+ final String newNodePath = parentPath + "/" + System.currentTimeMillis();
+ current = deepCreateNode(s, newNodePath);
+ }
+
+ // Copy request parameters to node properties and save
+ setPropertiesFromRequest(current, req);
+ s.save();
+ redirectPath = current.getPath();
+
+ } catch (RepositoryException re) {
+ throw new ServletException("Failed to modify content: "
+ + re.getMessage(), re);
+
+ } finally {
+ try {
+ if (s != null && s.hasPendingChanges()) {
+ s.refresh(false);
+ }
+ } catch (RepositoryException re) {
+ // TODO: might want to log, but don't further care
+ }
+ }
+
+ // redirect to the created node, so that it is displayed using a user-supplied extension
+ String redirectExtension = req.getParameter("slingDisplayExtension");
+ final String redirectUrl =
+ req.getContextPath() + req.getServletPath() + redirectPath
+ + (redirectExtension == null ? "" : "." + redirectExtension)
+ ;
+ resp.sendRedirect(redirectUrl);
+ }
+
+ /** Set node properties from current request (only handles Strings for now) */
+ protected void setPropertiesFromRequest(Node n, HttpServletRequest req)
+ throws RepositoryException {
+ // TODO ignore sling-specific properties like slingDisplayExtension
+ for (Enumeration e = req.getParameterNames(); e.hasMoreElements();) {
+ final String name = (String) e.nextElement();
+ final String[] values = req.getParameterValues(name);
+ if (values.length==1) {
+ n.setProperty(name, values[0]);
+ } else {
+ n.setProperty(name, values);
+ }
+ }
+ }
+
+ /**
+ * Deep creates a node, parent-padding with nt:unstructured nodes
+ *
+ * @param path absolute path to node that needs to be deep-created
+ */
+ protected Node deepCreateNode(Session s, String path)
+ throws RepositoryException {
+ String[] pathelems = path.substring(1).split("/");
+ int i = 0;
+ String mypath = "";
+ Node parent = s.getRootNode();
+ while (i < pathelems.length) {
+ String name = pathelems[i];
+ mypath += "/" + name;
+ if (!s.itemExists(mypath)) {
+ parent.addNode(name);
+ }
+ parent = (Node) s.getItem(mypath);
+ i++;
+ }
+ return (parent);
+ }
+
+ protected void dump(PrintWriter pw, Resource r, Node n) throws RepositoryException {
+ pw.println("** Node dumped by " + getClass().getSimpleName() + "**");
+ pw.println("Node path:" + n.getPath());
+ pw.println("Resource metadata: " + r.getMetadata());
+
+ pw.println("\n** Node properties **");
+ for (PropertyIterator pi = n.getProperties(); pi.hasNext();) {
+ final Property p = pi.nextProperty();
+ printPropertyValue(pw, p);
+ }
+ }
+
+ protected void dump(PrintWriter pw, Resource r, Property p) throws RepositoryException {
+ pw.println("** Property dumped by " + getClass().getSimpleName() + "**");
+ pw.println("Property path:" + p.getPath());
+ pw.println("Resource metadata: " + r.getMetadata());
+
+ printPropertyValue(pw, p);
+ }
+
+ protected void printPropertyValue(PrintWriter pw, Property p)
+ throws RepositoryException {
+
+ pw.print(p.getName() + ": ");
+
+ if (p.getDefinition().isMultiple()) {
+ Value[] values = p.getValues();
+ pw.print('[');
+ for (int i = 0; i < values.length; i++) {
+ if (i > 0) {
+ pw.print(", ");
+ }
+ pw.print(values[i].getString());
+ }
+ pw.println(']');
+ } else {
+ pw.println(p.getValue().getString());
+ }
+ }
+
+}
Added: sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/slingservlets/StreamServlet.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/slingservlets/StreamServlet.java?rev=1807480&view=auto
==============================================================================
--- sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/slingservlets/StreamServlet.java (added)
+++ sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/slingservlets/StreamServlet.java Wed Sep 6 13:24:23 2017
@@ -0,0 +1,225 @@
+/*
+ * $Url: $
+ * $Id: $
+ *
+ * Copyright 1997-2005 Day Management AG
+ * Barfuesserplatz 6, 4001 Basel, Switzerland
+ * All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Day Management AG, ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Day.
+ */
+package org.apache.sling.microsling.slingservlets;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import javax.jcr.Item;
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.sling.microsling.api.SlingRequestContext;
+import org.apache.sling.microsling.api.exceptions.HttpStatusCodeException;
+import org.apache.sling.microsling.helpers.constants.HttpConstants;
+import org.apache.sling.microsling.helpers.servlets.SlingSafeMethodsServlet;
+
+/**
+ * The <code>StreamServlet</code> handles requests for nodes which may just be
+ * streamed out to the response. If the requested JCR Item is an
+ * <em>nt:file</em> whose <em>jcr:content</em> child node is of type
+ * <em>nt:resource</em>, the response content type, last modification time and
+ * charcter encoding are set according to the resource node. In addition if
+ * the <em>If-Modified-Since</em> header is set, the resource will only be
+ * spooled if the last modification time is later than the header. Otherwise
+ * a 304 (Not Modified) status code is sent.
+ * <p>
+ * If the requested item is not an <em>nt:file</em>/<em>nt:resource</em> tuple,
+ * the item is just resolved by following the primary item trail according to
+ * the algorithm
+ * <pre>
+ * while (item.isNode) {
+ * item = ((Node) item).getPrimaryItem();
+ * }
+ * </pre>
+ * Until a property is found or the primary item is either not defined or not
+ * existing in which case an exception is thrown and the request fails with
+ * a 404 (Not Found) status.
+ */
+public class StreamServlet extends SlingSafeMethodsServlet {
+
+ @Override
+ protected void doGet(HttpServletRequest request,
+ HttpServletResponse response) throws ServletException, IOException {
+
+ final SlingRequestContext ctx = SlingRequestContext.getFromRequest(request);
+
+
+ try {
+ Item item = ctx.getResource().getItem();
+
+ // otherwise handle nt:file/nt:resource specially
+ Node node = (Node) item;
+ if (node.isNodeType("nt:file")) {
+ Node content = node.getNode("jcr:content");
+ if (content.isNodeType("nt:resource")) {
+
+ // check for if last modified
+ long ifModified = request.getDateHeader(HttpConstants.HEADER_IF_MODIFIED_SINCE);
+ long lastModified = getLastModified(content);
+ if (ifModified < 0 || lastModified > ifModified) {
+
+ String contentType = getMimeType(content);
+ if (contentType == null) {
+ contentType = ctx.getPreferredResponseContentType();
+ }
+
+ spool(response,
+ content.getProperty(JcrConstants.JCR_DATA),
+ contentType, getEncoding(content),
+ getLastModified(content));
+ } else {
+ response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
+ }
+
+ return;
+ }
+ }
+
+ // just spool, the property to which the item resolves through
+ // the primary item trail
+ // the item is a property, spool and forget
+ spool(response, findDataProperty(item), null, null, -1);
+
+ } catch (RepositoryException re) {
+ throw new HttpStatusCodeException(500, "RepositoryException in StreamServlet.doGet(): " + re.getMessage());
+ }
+ }
+
+ /**
+ * Spool the property value to the response setting the content type,
+ * character set, last modification data and content length header
+ */
+ private void spool(HttpServletResponse response, Property prop,
+ String mimeType, String encoding, long lastModified)
+ throws RepositoryException, IOException {
+
+ if (mimeType != null) {
+ response.setContentType(mimeType);
+ }
+
+ if (encoding != null) {
+ response.setCharacterEncoding(encoding);
+ }
+
+ if (lastModified > 0) {
+ response.setDateHeader(HttpConstants.HEADER_LAST_MODIFIED, lastModified);
+ }
+
+ // only set the content length if the property is a binary
+ if (prop.getType() == PropertyType.BINARY) {
+ response.setContentLength((int) prop.getLength());
+ }
+
+ InputStream ins = prop.getStream();
+ OutputStream out = null;
+ try {
+ ins = prop.getStream();
+ out = response.getOutputStream();
+
+ byte[] buf = new byte[2048];
+ int num;
+ while ((num = ins.read(buf)) >= 0) {
+ out.write(buf, 0, num);
+ }
+ } finally {
+ if (ins != null) {
+ try {
+ ins.close();
+ } catch (IOException ignore) {
+ }
+ }
+ if (out != null) {
+ try {
+ out.close();
+ } catch (IOException ignore) {
+ }
+ }
+ }
+ }
+
+ /** Find the Property that contains the data to spool, under parent */
+ private Property findDataProperty(final Item parent) throws RepositoryException, HttpStatusCodeException {
+ Property result = null;
+
+ // Following the path of primary items until we find a property
+ // should provide us with the file data of the parent
+ try {
+ Item item = parent;
+ while(item!=null && item.isNode()) {
+ item = ((Node) item).getPrimaryItem();
+ }
+ result = (Property)item;
+ } catch(ItemNotFoundException ignored) {
+ // TODO: for now we use an alternate method if this fails,
+ // there might be a better way (see jackrabbit WebDAV server code?)
+ }
+
+ if(result==null && parent.isNode()) {
+ // primary path didn't work, try the "usual" path to the data Property
+ try {
+ final Node parentNode = (Node)parent;
+ result = parentNode.getNode("jcr:content").getProperty("jcr:data");
+ } catch(ItemNotFoundException e) {
+ throw new HttpStatusCodeException(404,parent.getPath() + "/jcr:content" + "/jcr:data");
+ }
+ }
+
+ if(result==null) {
+ throw new HttpStatusCodeException(500, "Unable to find data property for parent item " + parent.getPath());
+ }
+
+ return result;
+ }
+
+ /** return the jcr:lastModified property value or null if property is missing */
+ private long getLastModified(Node resourceNode) throws RepositoryException {
+ Property lastModifiedProp = getProperty(resourceNode,
+ JcrConstants.JCR_LASTMODIFIED);
+ return (lastModifiedProp != null) ? lastModifiedProp.getLong() : -1;
+ }
+
+ /** return the jcr:mimeType property value or null if property is missing */
+ private String getMimeType(Node resourceNode) throws RepositoryException {
+ Property mimeTypeProp = getProperty(resourceNode,
+ JcrConstants.JCR_MIMETYPE);
+ return (mimeTypeProp != null) ? mimeTypeProp.getString() : null;
+ }
+
+ /** return the jcr:encoding property value or null if property is missing */
+ private String getEncoding(Node resourceNode) throws RepositoryException {
+ Property encodingProp = getProperty(resourceNode,
+ JcrConstants.JCR_ENCODING);
+ return (encodingProp != null) ? encodingProp.getString() : null;
+ }
+
+ /** Return the named property or null if not existing or node is null */
+ private Property getProperty(Node node, String relPath)
+ throws RepositoryException {
+ if (node != null && node.hasProperty(relPath)) {
+ return node.getProperty(relPath);
+ }
+
+ return null;
+ }
+}
Added: sling/whiteboard/microsling/src/main/webapp/WEB-INF/log4j.dtd
URL: http://svn.apache.org/viewvc/sling/whiteboard/microsling/src/main/webapp/WEB-INF/log4j.dtd?rev=1807480&view=auto
==============================================================================
--- sling/whiteboard/microsling/src/main/webapp/WEB-INF/log4j.dtd (added)
+++ sling/whiteboard/microsling/src/main/webapp/WEB-INF/log4j.dtd Wed Sep 6 13:24:23 2017
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!-- Authors: Chris Taylor, Ceki Gulcu. -->
+
+<!-- Version: 1.2 -->
+
+<!-- A configuration element consists of optional renderer
+elements,appender elements, categories and an optional root
+element. -->
+
+<!ELEMENT log4j:configuration (renderer*, appender*, logger*, root?)>
+
+<!-- The "threshold" attribute takes a level value such that all -->
+<!-- logging statements with a level equal or below this value are -->
+<!-- disabled. -->
+
+<!-- Setting the "debug" enable the printing of internal log4j logging -->
+<!-- statements. -->
+
+<!-- By default, debug attribute is "null", meaning that we not do touch -->
+<!-- internal log4j logging settings. The "null" value for the threshold -->
+<!-- attribute can be misleading. The threshold field of a repository -->
+<!-- cannot be set to null. The "null" value for the threshold attribute -->
+<!-- simply means don't touch the threshold field, the threshold field -->
+<!-- keeps its old value. -->
+
+<!ATTLIST log4j:configuration
+ xmlns:log4j CDATA #FIXED "http://jakarta.apache.org/log4j/"
+ threshold (all|debug|info|warn|error|fatal|off|null) "null"
+ debug (true|false|null) "null"
+>
+
+<!-- renderer elements allow the user to customize the conversion of -->
+<!-- message objects to String. -->
+
+<!ELEMENT renderer EMPTY>
+<!ATTLIST renderer
+ renderedClass CDATA #REQUIRED
+ renderingClass CDATA #REQUIRED
+>
+
+<!-- Appenders must have a name and a class. -->
+<!-- Appenders may contain an error handler, a layout, optional parameters -->
+<!-- and filters. They may also reference (or include) other appenders. -->
+<!ELEMENT appender (errorHandler?, param*, layout?, filter*, appender-ref*)>
+<!ATTLIST appender
+ name ID #REQUIRED
+ class CDATA #REQUIRED
+>
+
+<!ELEMENT layout (param*)>
+<!ATTLIST layout
+ class CDATA #REQUIRED
+>
+
+<!ELEMENT filter (param*)>
+<!ATTLIST filter
+ class CDATA #REQUIRED
+>
+
+<!-- ErrorHandlers can be of any class. They can admit any number of -->
+<!-- parameters. -->
+
+<!ELEMENT errorHandler (param*, root-ref?, logger-ref*, appender-ref?)>
+<!ATTLIST errorHandler
+ class CDATA #REQUIRED
+>
+
+<!ELEMENT root-ref EMPTY>
+
+<!ELEMENT logger-ref EMPTY>
+<!ATTLIST logger-ref
+ ref IDREF #REQUIRED
+>
+
+<!ELEMENT param EMPTY>
+<!ATTLIST param
+ name CDATA #REQUIRED
+ value CDATA #REQUIRED
+>
+
+
+<!-- The priority class is org.apache.log4j.Level by default -->
+<!--
+ DEPRECATED
+
+<!ELEMENT priority (param*)>
+<!ATTLIST priority
+ class CDATA #IMPLIED
+ value CDATA #REQUIRED
+>
+-->
+
+<!-- The level class is org.apache.log4j.Level by default -->
+<!ELEMENT level (param*)>
+<!ATTLIST level
+ class CDATA #IMPLIED
+ value CDATA #REQUIRED
+>
+
+
+<!-- If no level element is specified, then the configurator MUST not -->
+<!-- touch the level of the named category. -->
+<!--
+ DEPRECATED
+
+<!ELEMENT category (param*,(priority|level)?,appender-ref*)>
+<!ATTLIST category
+ class CDATA #IMPLIED
+ name CDATA #REQUIRED
+ additivity (true|false) "true"
+>
+-->
+
+<!-- If no level element is specified, then the configurator MUST not -->
+<!-- touch the level of the named logger. -->
+<!ELEMENT logger (level?, appender-ref*)>
+<!ATTLIST logger
+ class CDATA #IMPLIED
+ name ID #REQUIRED
+ additivity (true|false) "true"
+>
+
+<!--
+ DEPRECATED
+
+<!ELEMENT categoryFactory (param*)>
+<!ATTLIST categoryFactory
+ class CDATA #REQUIRED>
+-->
+
+<!ELEMENT appender-ref EMPTY>
+<!ATTLIST appender-ref
+ ref IDREF #REQUIRED
+>
+
+<!-- If no priority element is specified, then the configurator MUST not -->
+<!-- touch the priority of root. -->
+<!-- The root category always exists and cannot be subclassed. -->
+<!ELEMENT root (param*, level?, appender-ref*)>
+
+
+<!-- ==================================================================== -->
+<!-- A logging event -->
+<!-- ==================================================================== -->
+<!ELEMENT log4j:eventSet (log4j:event*)>
+<!ATTLIST log4j:eventSet
+ xmlns:log4j CDATA #FIXED "http://jakarta.apache.org/log4j/"
+ version (1.1|1.2) "1.2"
+ includesLocationInfo (true|false) "true"
+>
+
+
+
+<!ELEMENT log4j:event (log4j:message, log4j:NDC?, log4j:throwable?,
+ log4j:locationInfo?) >
+
+<!-- The timestamp format is application dependent. -->
+<!ATTLIST log4j:event
+ logger CDATA #REQUIRED
+ level CDATA #REQUIRED
+ thread CDATA #REQUIRED
+ timestamp CDATA #REQUIRED
+>
+
+<!ELEMENT log4j:message (#PCDATA)>
+<!ELEMENT log4j:NDC (#PCDATA)>
+
+<!ELEMENT log4j:throwable (#PCDATA)>
+
+<!ELEMENT log4j:locationInfo EMPTY>
+<!ATTLIST log4j:locationInfo
+ class CDATA #REQUIRED
+ method CDATA #REQUIRED
+ file CDATA #REQUIRED
+ line CDATA #REQUIRED
+>
Added: sling/whiteboard/microsling/src/main/webapp/WEB-INF/log4j.xml
URL: http://svn.apache.org/viewvc/sling/whiteboard/microsling/src/main/webapp/WEB-INF/log4j.xml?rev=1807480&view=auto
==============================================================================
--- sling/whiteboard/microsling/src/main/webapp/WEB-INF/log4j.xml (added)
+++ sling/whiteboard/microsling/src/main/webapp/WEB-INF/log4j.xml Wed Sep 6 13:24:23 2017
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+ 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.
+ -->
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+
+ <!-- ====================================================================== -->
+ <!-- A P P E N D E R S -->
+ <!-- ====================================================================== -->
+
+ <!-- console -->
+ <appender name="console" class="org.apache.log4j.ConsoleAppender">
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="%d{dd.MM.yyyy HH:mm:ss} *%-5p* %c{1}: %m (%F, line %L)%n"/>
+ </layout>
+ </appender>
+
+ <!-- log file -->
+ <appender name="file" class="org.apache.log4j.FileAppender">
+ <param name="File" value="logs/microsling.log"/>
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="%d{dd.MM.yyyy HH:mm:ss} *%-5p* %c{1}: %m (%F, line %L)%n"/>
+ </layout>
+ </appender>
+
+ <!-- ====================================================================== -->
+ <!-- C A T E G O R I E S -->
+ <!-- ====================================================================== -->
+ <category name="org.apache.sling">
+ <priority value="debug"/>
+ <appender-ref ref="console"/>
+ <appender-ref ref="file"/>
+ </category>
+
+</log4j:configuration>