You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by fm...@apache.org on 2013/05/27 14:34:47 UTC

svn commit: r1486594 - in /felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl: ./ helper/

Author: fmeschbe
Date: Mon May 27 12:34:46 2013
New Revision: 1486594

URL: http://svn.apache.org/r1486594
Log:
FELIX-4075 Fix Configuration ZIP writing

- Consolidate ConfigurationWriter classes into a new
  helpers package (out of the AbstractWebConsolePlugin)
- Create a dead-simple SimpleJson helper class to
  generate the configuration ZIP index.json file
- Create an index.json file providing the timestamp
  and the files provided by the plugins
- Use "<pre>" formatting for plain text and JSON
  output in the HTML (Browser) context

Added:
    felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/helper/
    felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/helper/ConfigurationWriter.java
    felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/helper/HtmlConfigurationWriter.java
    felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/helper/JSONConfigurationWriter.java
    felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/helper/PlainTextConfigurationWriter.java
    felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/helper/SimpleJson.java
    felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/helper/ZipConfigurationWriter.java
Modified:
    felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/AbstractWebConsolePlugin.java
    felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/DefaultWebConsolePlugin.java
    felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/InventoryPrinterAdapter.java
    felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/InventoryPrinterManagerImpl.java

Modified: felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/AbstractWebConsolePlugin.java
URL: http://svn.apache.org/viewvc/felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/AbstractWebConsolePlugin.java?rev=1486594&r1=1486593&r2=1486594&view=diff
==============================================================================
--- felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/AbstractWebConsolePlugin.java (original)
+++ felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/AbstractWebConsolePlugin.java Mon May 27 12:34:46 2013
@@ -17,21 +17,20 @@
 package org.apache.felix.inventory.impl;
 
 import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.io.Writer;
+import java.text.DateFormat;
 import java.util.Date;
-import java.util.StringTokenizer;
-import java.util.zip.Deflater;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipOutputStream;
+import java.util.Locale;
 
-import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.felix.inventory.Format;
+import org.apache.felix.inventory.impl.helper.ConfigurationWriter;
+import org.apache.felix.inventory.impl.helper.HtmlConfigurationWriter;
+import org.apache.felix.inventory.impl.helper.JSONConfigurationWriter;
+import org.apache.felix.inventory.impl.helper.PlainTextConfigurationWriter;
+import org.apache.felix.inventory.impl.helper.ZipConfigurationWriter;
 
 /**
  * The web console plugin for a inventory printer.
@@ -69,7 +68,7 @@ public abstract class AbstractWebConsole
         }
         else
         {
-            if (handler.supports(format))
+            if (format == null || handler.supports(format))
             {
                 pw.printInventory(format, handler);
             }
@@ -101,8 +100,7 @@ public abstract class AbstractWebConsole
         return null; // all by default
     }
 
-    protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException,
-        IOException
+    protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws IOException
     {
         this.setNoCache(response);
 
@@ -140,33 +138,9 @@ public abstract class AbstractWebConsole
             }
             response.setContentType(type);
 
-            final ZipOutputStream zip = new ZipOutputStream(response.getOutputStream());
-            zip.setLevel(Deflater.BEST_SPEED);
-            zip.setMethod(ZipOutputStream.DEFLATED);
-
-            final Date now = new Date();
-            // create time stamp entry
-            final ZipEntry entry = new ZipEntry("timestamp.txt"); //$NON-NLS-2$
-            entry.setTime(now.getTime());
-            zip.putNextEntry(entry);
-            final StringBuffer sb = new StringBuffer();
-            sb.append("Date: ");
-            synchronized (InventoryPrinterAdapter.DISPLAY_DATE_FORMAT)
-            {
-                sb.append(InventoryPrinterAdapter.DISPLAY_DATE_FORMAT.format(now));
-            }
-            sb.append(" (");
-            sb.append(String.valueOf(now.getTime()));
-            sb.append(")\n");
-
-            zip.write(sb.toString().getBytes("UTF-8"));
-            zip.closeEntry();
-
-            final ZipConfigurationWriter pw = new ZipConfigurationWriter(zip);
-            printConfigurationInventory(pw, Format.TEXT, handler);
-            printConfigurationInventory(pw, Format.JSON, handler);
-
-            zip.finish();
+            final ZipConfigurationWriter pw = ZipConfigurationWriter.create(response.getOutputStream());
+            printConfigurationInventory(pw, null, handler);
+            pw.finish();
         }
         else if (request.getPathInfo().endsWith(".nfo"))
         {
@@ -259,12 +233,8 @@ public abstract class AbstractWebConsole
             pw.println("</script>");
             pw.println("<br/><p class=\"statline\">");
 
-            final Date currentTime = new Date();
-            synchronized (InventoryPrinterAdapter.DISPLAY_DATE_FORMAT)
-            {
-                pw.print("Date: ");
-                pw.println(InventoryPrinterAdapter.DISPLAY_DATE_FORMAT.format(currentTime));
-            }
+            pw.print("Date: ");
+            pw.println(DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, Locale.US).format(new Date()));
 
             pw.print("<button type=\"button\" class=\"downloadFullZip\" style=\"float: right; margin-right: 30px; margin-top: 5px;\">Download Full Zip</button>");
             pw.print("<button type=\"button\" class=\"downloadFullTxt\" style=\"float: right; margin-right: 30px; margin-top: 5px;\">Download Full Text</button>");
@@ -281,398 +251,38 @@ public abstract class AbstractWebConsole
 
             pw.println("<br/>&nbsp;</p>"); // status line
             pw.print("<div>");
-            if (handler.supports(Format.HTML))
-            {
-                handler.print(pw, Format.HTML, false);
-            }
-            else
-            {
-                pw.enableFilter(true);
-                handler.print(pw, Format.TEXT, false);
-                pw.enableFilter(false);
-            }
-            pw.print("</div>");
-        }
-    }
 
-    /**
-     * Base class for all configuration writers.
-     */
-    private abstract static class ConfigurationWriter extends PrintWriter
-    {
-
-        ConfigurationWriter(final Writer delegatee)
-        {
-            super(delegatee);
-        }
-
-        protected void title(final String title) throws IOException
-        {
-            // dummy implementation
-        }
-
-        protected void end() throws IOException
-        {
-            // dummy implementation
-        }
-
-        public void printInventory(final Format format, final InventoryPrinterHandler handler) throws IOException
-        {
-            this.title(handler.getTitle());
-            handler.print(this, format, false);
-            this.end();
-        }
-    }
-
-    /**
-     * The JSON configuration writer
-     */
-    private static class JSONConfigurationWriter extends ConfigurationWriter
-    {
-
-        private boolean wrapJSON;
-
-        private boolean startLine;
-
-        private boolean needComma;
-
-        JSONConfigurationWriter(final Writer delegatee)
-        {
-            super(delegatee);
-            this.wrapJSON = false;
-        }
-
-        public void startJSONWrapper()
-        {
-            println("{");
-            println("  \"value\": [");
-
-            this.wrapJSON = true;
-            this.startLine = true;
-            this.needComma = false;
-        }
-
-        public void endJSONWrapper()
-        {
-            if (this.wrapJSON)
-            {
-                // properly terminate the current line
-                this.println();
-
-                this.wrapJSON = false;
-                this.startLine = false;
-
-                super.println();
-                super.println("  ]");
-                super.println("}");
-            }
-        }
-
-        // IE has an issue with white-space:pre in our case so, we write
-        // <br/> instead of [CR]LF to get the line break. This also works
-        // in other browsers.
-        public void println()
-        {
-            if (wrapJSON)
-            {
-                if (!this.startLine)
-                {
-                    super.write('"');
-                    this.startLine = true;
-                    this.needComma = true;
-                }
-            }
-            else
-            {
-                super.println();
-            }
-        }
-
-        // some VM implementation directly write in underlying stream, instead
-        // of
-        // delegation to the write() method. So we need to override this, to
-        // make
-        // sure, that everything is escaped correctly
-        public void print(final String str)
-        {
-            final char[] chars = str.toCharArray();
-            write(chars, 0, chars.length);
-        }
-
-        private final char[] oneChar = new char[1];
-
-        // always delegate to write(char[], int, int) otherwise in some VM
-        // it cause endless cycle and StackOverflowError
-        public void write(final int character)
-        {
-            synchronized (oneChar)
-            {
-                oneChar[0] = (char) character;
-                write(oneChar, 0, 1);
-            }
-        }
-
-        // write the characters unmodified unless filtering is enabled in
-        // which case the writeFiltered(String) method is called for filtering
-        public void write(char[] chars, int off, int len)
-        {
-            if (this.wrapJSON)
-            {
-                if (this.startLine)
-                {
-                    this.startLine();
-                    this.startLine = false;
-                }
-
-                String v = new String(chars, off, len);
-                StringTokenizer st = new StringTokenizer(v, "\r\n\"", true);
-                while (st.hasMoreTokens())
-                {
-                    String t = st.nextToken();
-                    if (t.length() == 1)
-                    {
-                        char c = t.charAt(0);
-                        if (c == '\r')
-                        {
-                            // ignore
-                        }
-                        else if (c == '\n')
-                        {
-                            this.println();
-                            this.startLine();
-                        }
-                        else if (c == '"')
-                        {
-                            super.write('\\');
-                            super.write(c);
-                        }
-                        else
-                        {
-                            super.write(c);
-                        }
-                    }
-                    else
-                    {
-                        super.write(t.toCharArray(), 0, t.length());
-                    }
-                }
-            }
-            else
+            final boolean filter;
+            final Format format;
+            if (handler.supports(Format.HTML))
             {
-                super.write(chars, off, len);
+                filter = false;
+                format = Format.HTML;
             }
-        }
-
-        // write the string unmodified unless filtering is enabled in
-        // which case the writeFiltered(String) method is called for filtering
-        public void write(final String string, final int off, final int len)
-        {
-            write(string.toCharArray(), off, len);
-        }
-
-        private void startLine()
-        {
-            if (this.needComma)
+            else if (handler.supports(Format.TEXT))
             {
-                super.write(',');
-                super.println();
-                this.needComma = false;
+                // prefer TEXT of JSON if available
+                filter = true;
+                format = Format.TEXT;
             }
-
-            super.write("    \"".toCharArray(), 0, 5);
-            this.startLine = false;
-        }
-    }
-
-    /**
-     * The HTML configuration writer outputs the status as an HTML snippet.
-     */
-    private static class HtmlConfigurationWriter extends ConfigurationWriter
-    {
-
-        // whether or not to filter "<" signs in the output
-        private boolean doFilter;
-
-        HtmlConfigurationWriter(final Writer delegatee)
-        {
-            super(delegatee);
-        }
-
-        void enableFilter(final boolean doFilter)
-        {
-            this.doFilter = doFilter;
-        }
-
-        // IE has an issue with white-space:pre in our case so, we write
-        // <br/> instead of [CR]LF to get the line break. This also works
-        // in other browsers.
-        public void println()
-        {
-            if (doFilter)
+            else if (handler.supports(Format.JSON))
             {
-                this.write('\n'); // write <br/>
+                filter = true;
+                format = Format.JSON;
             }
             else
             {
-                super.println();
-            }
-        }
-
-        // some VM implementation directly write in underlying stream, instead
-        // of
-        // delegation to the write() method. So we need to override this, to
-        // make
-        // sure, that everything is escaped correctly
-        public void print(final String str)
-        {
-            final char[] chars = str.toCharArray();
-            write(chars, 0, chars.length);
-        }
-
-        private final char[] oneChar = new char[1];
-
-        // always delegate to write(char[], int, int) otherwise in some VM
-        // it cause endless cycle and StackOverflowError
-        public void write(final int character)
-        {
-            synchronized (oneChar)
-            {
-                oneChar[0] = (char) character;
-                write(oneChar, 0, 1);
-            }
-        }
-
-        // write the characters unmodified unless filtering is enabled in
-        // which case the writeFiltered(String) method is called for filtering
-        public void write(char[] chars, int off, int len)
-        {
-            if (doFilter)
-            {
-                chars = this.escapeHtml(new String(chars, off, len)).toCharArray();
-                off = 0;
-                len = chars.length;
-            }
-            super.write(chars, off, len);
-        }
-
-        // write the string unmodified unless filtering is enabled in
-        // which case the writeFiltered(String) method is called for filtering
-        public void write(final String string, final int off, final int len)
-        {
-            write(string.toCharArray(), off, len);
-        }
-
-        /**
-         * Escapes HTML special chars like: <>&\r\n and space
-         *
-         *
-         * @param text the text to escape
-         * @return the escaped text
-         */
-        private String escapeHtml(final String text)
-        {
-            final StringBuffer sb = new StringBuffer(text.length() * 4 / 3);
-            char ch, oldch = '_';
-            for (int i = 0; i < text.length(); i++)
-            {
-                switch (ch = text.charAt(i))
-                {
-                    case '<':
-                        sb.append("&lt;"); //$NON-NLS-1$
-                        break;
-                    case '>':
-                        sb.append("&gt;"); //$NON-NLS-1$
-                        break;
-                    case '&':
-                        sb.append("&amp;"); //$NON-NLS-1$
-                        break;
-                    case ' ':
-                        sb.append("&nbsp;"); //$NON-NLS-1$
-                        break;
-                    case '\r':
-                    case '\n':
-                        if (oldch != '\r' && oldch != '\n') // don't add twice
-                                                            // <br>
-                            sb.append("<br/>\n"); //$NON-NLS-1$
-                        break;
-                    default:
-                        sb.append(ch);
-                }
-                oldch = ch;
+                // fallback to TEXT (if unknown format)
+                filter = true;
+                format = Format.TEXT;
             }
 
-            return sb.toString();
-        }
-    }
-
-    /**
-     * The plain text configuration writer outputs the status as plain text.
-     */
-    private static class PlainTextConfigurationWriter extends ConfigurationWriter
-    {
-
-        PlainTextConfigurationWriter(final Writer delegatee)
-        {
-            super(delegatee);
-        }
-
-        protected void title(final String title) throws IOException
-        {
-            print("*** ");
-            print(title);
-            println(":");
-        }
+            pw.enableFilter(filter);
+            handler.print(pw, format, false);
+            pw.enableFilter(false);
 
-        protected void end() throws IOException
-        {
-            println();
+            pw.print("</div>");
         }
     }
 
-    /**
-     * The ZIP configuration writer creates a zip with
-     * - txt output of a inventory printers (if supported)
-     * - json output of a inventory printers (if supported)
-     * - attachments from a inventory printer (if supported)
-     */
-    private static class ZipConfigurationWriter extends ConfigurationWriter
-    {
-
-        private final ZipOutputStream zip;
-
-        ZipConfigurationWriter(final ZipOutputStream zip)
-        {
-            super(new OutputStreamWriter(zip));
-            this.zip = zip;
-        }
-
-        public void printInventory(final Format format, final InventoryPrinterHandler handler) throws IOException
-        {
-            if (format == Format.TEXT)
-            {
-                final ZipEntry entry = new ZipEntry(handler.getName().concat(".txt"));
-                zip.putNextEntry(entry);
-                handler.print(this, format, false);
-                flush();
-                zip.closeEntry();
-
-                handler.addAttachments(this.zip, handler.getName().concat("/"));
-            }
-            else if (format == Format.JSON)
-            {
-                final String name = "json/".concat(handler.getName()).concat(".json");
-
-                final ZipEntry entry = new ZipEntry(name);
-                zip.putNextEntry(entry);
-                handler.print(this, Format.JSON, true);
-                flush();
-
-                zip.closeEntry();
-                if (!handler.supports(Format.TEXT))
-                {
-                    handler.addAttachments(this.zip, handler.getName().concat("/"));
-                }
-            }
-        }
-    }
 }
\ No newline at end of file

Modified: felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/DefaultWebConsolePlugin.java
URL: http://svn.apache.org/viewvc/felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/DefaultWebConsolePlugin.java?rev=1486594&r1=1486593&r2=1486594&view=diff
==============================================================================
--- felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/DefaultWebConsolePlugin.java (original)
+++ felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/DefaultWebConsolePlugin.java Mon May 27 12:34:46 2013
@@ -90,7 +90,7 @@ public class DefaultWebConsolePlugin ext
      */
     public void print(final PrintWriter printWriter, final Format format, final boolean isZip)
     {
-        final InventoryPrinterHandler[] handlers = this.inventoryPrinterManager.getAllHandlers();
+        final InventoryPrinterHandler[] handlers = this.inventoryPrinterManager.getHandlers(null);
         printWriter.print("Currently registered ");
         printWriter.print(String.valueOf(handlers.length));
         printWriter.println(" printer(s).");

Modified: felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/InventoryPrinterAdapter.java
URL: http://svn.apache.org/viewvc/felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/InventoryPrinterAdapter.java?rev=1486594&r1=1486593&r2=1486594&view=diff
==============================================================================
--- felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/InventoryPrinterAdapter.java (original)
+++ felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/InventoryPrinterAdapter.java Mon May 27 12:34:46 2013
@@ -18,9 +18,7 @@ package org.apache.felix.inventory.impl;
 
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.text.DateFormat;
 import java.util.Comparator;
-import java.util.Locale;
 import java.util.zip.ZipOutputStream;
 
 import org.apache.felix.inventory.InventoryPrinter;
@@ -38,12 +36,6 @@ public class InventoryPrinterAdapter imp
 {
 
     /**
-     * Formatter pattern to render the current time of inventory generation.
-     */
-    static final DateFormat DISPLAY_DATE_FORMAT = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG,
-        Locale.US);
-
-    /**
      * Comparator for adapters based on the service ranking.
      */
     public static final Comparator RANKING_COMPARATOR = new Comparator()

Modified: felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/InventoryPrinterManagerImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/InventoryPrinterManagerImpl.java?rev=1486594&r1=1486593&r2=1486594&view=diff
==============================================================================
--- felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/InventoryPrinterManagerImpl.java (original)
+++ felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/InventoryPrinterManagerImpl.java Mon May 27 12:34:46 2013
@@ -262,21 +262,13 @@ public class InventoryPrinterManagerImpl
     }
 
     /**
-     * Get all inventory printer handlers.
+     * Get all handlers supporting the format or all handlers if {@code format}
+     * is {@code null}.
      *
-     * @return A list of handlers - might be empty.
-     */
-    public InventoryPrinterHandler[] getAllHandlers()
-    {
-        synchronized (this.usedAdapters)
-        {
-            return (InventoryPrinterHandler[]) this.usedAdapters.toArray(new InventoryPrinterHandler[this.usedAdapters
-                .size()]);
-        }
-    }
-
-    /**
-     * Get all handlers supporting the format.
+     * @param format The {@link Format} the returned handlers are expected to
+     *            support. If this parameter is {@code null} all handlers are
+     *            returned regardless of {@link Format} supported by the
+     *            handlers.
      *
      * @return A list of handlers - might be empty.
      */
@@ -289,7 +281,7 @@ public class InventoryPrinterManagerImpl
             while (i.hasNext())
             {
                 final InventoryPrinterAdapter printer = (InventoryPrinterAdapter) i.next();
-                if (printer.supports(format))
+                if (format == null || printer.supports(format))
                 {
                     result.add(printer);
                 }

Added: felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/helper/ConfigurationWriter.java
URL: http://svn.apache.org/viewvc/felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/helper/ConfigurationWriter.java?rev=1486594&view=auto
==============================================================================
--- felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/helper/ConfigurationWriter.java (added)
+++ felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/helper/ConfigurationWriter.java Mon May 27 12:34:46 2013
@@ -0,0 +1,55 @@
+/*
+ * 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.felix.inventory.impl.helper;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Writer;
+
+import org.apache.felix.inventory.Format;
+import org.apache.felix.inventory.impl.InventoryPrinterHandler;
+
+/**
+ * Base class for all configuration writers.
+ */
+public abstract class ConfigurationWriter extends PrintWriter
+{
+
+    ConfigurationWriter(final Writer delegatee)
+    {
+        super(delegatee);
+    }
+
+    protected void title(final String title) throws IOException
+    {
+        // dummy implementation
+    }
+
+    protected void end() throws IOException
+    {
+        // dummy implementation
+    }
+
+    public void printInventory(final Format format, final InventoryPrinterHandler handler) throws IOException
+    {
+        this.title(handler.getTitle());
+        handler.print(this, format, false);
+        this.end();
+    }
+}
\ No newline at end of file

Added: felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/helper/HtmlConfigurationWriter.java
URL: http://svn.apache.org/viewvc/felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/helper/HtmlConfigurationWriter.java?rev=1486594&view=auto
==============================================================================
--- felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/helper/HtmlConfigurationWriter.java (added)
+++ felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/helper/HtmlConfigurationWriter.java Mon May 27 12:34:46 2013
@@ -0,0 +1,150 @@
+/*
+ * 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.felix.inventory.impl.helper;
+
+import java.io.Writer;
+
+
+/**
+ * The HTML configuration writer outputs the status as an HTML snippet.
+ */
+public class HtmlConfigurationWriter extends ConfigurationWriter
+{
+
+    // whether or not to filter "<" signs in the output
+    private boolean doFilter;
+
+    public HtmlConfigurationWriter(final Writer delegatee)
+    {
+        super(delegatee);
+    }
+
+    public void enableFilter(final boolean doFilter)
+    {
+        this.doFilter = doFilter;
+        if (doFilter) {
+            // start filtering
+            super.write("<pre>", 0, 5);
+        } else {
+            // end filtering
+            super.write("</pre>", 0, 6);
+        }
+        super.println();
+    }
+
+    // IE has an issue with white-space:pre in our case so, we write
+    // <br/> instead of [CR]LF to get the line break. This also works
+    // in other browsers.
+    public void println()
+    {
+        if (doFilter)
+        {
+            this.write('\n'); // write <br/>
+        }
+        else
+        {
+            super.println();
+        }
+    }
+
+    // some VM implementation directly write in underlying stream, instead
+    // of
+    // delegation to the write() method. So we need to override this, to
+    // make
+    // sure, that everything is escaped correctly
+    public void print(final String str)
+    {
+        final char[] chars = str.toCharArray();
+        write(chars, 0, chars.length);
+    }
+
+    private final char[] oneChar = new char[1];
+
+    // always delegate to write(char[], int, int) otherwise in some VM
+    // it cause endless cycle and StackOverflowError
+    public void write(final int character)
+    {
+        synchronized (oneChar)
+        {
+            oneChar[0] = (char) character;
+            write(oneChar, 0, 1);
+        }
+    }
+
+    // write the characters unmodified unless filtering is enabled in
+    // which case the writeFiltered(String) method is called for filtering
+    public void write(char[] chars, int off, int len)
+    {
+        if (doFilter)
+        {
+            chars = this.escapeHtml(new String(chars, off, len)).toCharArray();
+            off = 0;
+            len = chars.length;
+        }
+        super.write(chars, off, len);
+    }
+
+    // write the string unmodified unless filtering is enabled in
+    // which case the writeFiltered(String) method is called for filtering
+    public void write(final String string, final int off, final int len)
+    {
+        write(string.toCharArray(), off, len);
+    }
+
+    /**
+     * Escapes HTML special chars like: <>&\r\n and space
+     *
+     *
+     * @param text the text to escape
+     * @return the escaped text
+     */
+    private String escapeHtml(final String text)
+    {
+        final StringBuffer sb = new StringBuffer(text.length() * 4 / 3);
+        char ch, oldch = '_';
+        for (int i = 0; i < text.length(); i++)
+        {
+            switch (ch = text.charAt(i))
+            {
+                case '<':
+                    sb.append("&lt;"); //$NON-NLS-1$
+                    break;
+                case '>':
+                    sb.append("&gt;"); //$NON-NLS-1$
+                    break;
+                case '&':
+                    sb.append("&amp;"); //$NON-NLS-1$
+                    break;
+                case ' ':
+                    sb.append("&nbsp;"); //$NON-NLS-1$
+                    break;
+                case '\r':
+                case '\n':
+                    if (oldch != '\r' && oldch != '\n')
+                        sb.append("\n"); //$NON-NLS-1$
+                    break;
+                default:
+                    sb.append(ch);
+            }
+            oldch = ch;
+        }
+
+        return sb.toString();
+    }
+}
\ No newline at end of file

Added: felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/helper/JSONConfigurationWriter.java
URL: http://svn.apache.org/viewvc/felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/helper/JSONConfigurationWriter.java?rev=1486594&view=auto
==============================================================================
--- felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/helper/JSONConfigurationWriter.java (added)
+++ felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/helper/JSONConfigurationWriter.java Mon May 27 12:34:46 2013
@@ -0,0 +1,185 @@
+/*
+ * 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.felix.inventory.impl.helper;
+
+import java.io.Writer;
+import java.util.StringTokenizer;
+
+
+/**
+ * The JSON configuration writer
+ */
+public class JSONConfigurationWriter extends ConfigurationWriter
+{
+
+    private boolean wrapJSON;
+
+    private boolean startLine;
+
+    private boolean needComma;
+
+    public JSONConfigurationWriter(final Writer delegatee)
+    {
+        super(delegatee);
+        this.wrapJSON = false;
+    }
+
+    public void startJSONWrapper()
+    {
+//        println("{");
+//        println("  \"value\": [");
+        println("[");
+
+        this.wrapJSON = true;
+        this.startLine = true;
+        this.needComma = false;
+    }
+
+    public void endJSONWrapper()
+    {
+        if (this.wrapJSON)
+        {
+            // properly terminate the current line
+            this.println();
+
+            this.wrapJSON = false;
+            this.startLine = false;
+
+//            super.println();
+//            super.println("  ]");
+//            super.println("}");
+            super.println("]");
+        }
+    }
+
+    // IE has an issue with white-space:pre in our case so, we write
+    // <br/> instead of [CR]LF to get the line break. This also works
+    // in other browsers.
+    public void println()
+    {
+        if (wrapJSON)
+        {
+            if (!this.startLine)
+            {
+                super.write('"');
+                this.startLine = true;
+                this.needComma = true;
+            }
+        }
+        else
+        {
+            super.println();
+        }
+    }
+
+    // some VM implementation directly write in underlying stream, instead
+    // of
+    // delegation to the write() method. So we need to override this, to
+    // make
+    // sure, that everything is escaped correctly
+    public void print(final String str)
+    {
+        final char[] chars = str.toCharArray();
+        write(chars, 0, chars.length);
+    }
+
+    private final char[] oneChar = new char[1];
+
+    // always delegate to write(char[], int, int) otherwise in some VM
+    // it cause endless cycle and StackOverflowError
+    public void write(final int character)
+    {
+        synchronized (oneChar)
+        {
+            oneChar[0] = (char) character;
+            write(oneChar, 0, 1);
+        }
+    }
+
+    // write the characters unmodified unless filtering is enabled in
+    // which case the writeFiltered(String) method is called for filtering
+    public void write(char[] chars, int off, int len)
+    {
+        if (this.wrapJSON)
+        {
+            if (this.startLine)
+            {
+                this.startLine();
+                this.startLine = false;
+            }
+
+            String v = new String(chars, off, len);
+            StringTokenizer st = new StringTokenizer(v, "\r\n\"", true);
+            while (st.hasMoreTokens())
+            {
+                String t = st.nextToken();
+                if (t.length() == 1)
+                {
+                    char c = t.charAt(0);
+                    if (c == '\r')
+                    {
+                        // ignore
+                    }
+                    else if (c == '\n')
+                    {
+                        this.println();
+                        this.startLine();
+                    }
+                    else if (c == '"')
+                    {
+                        super.write('\\');
+                        super.write(c);
+                    }
+                    else
+                    {
+                        super.write(c);
+                    }
+                }
+                else
+                {
+                    super.write(t.toCharArray(), 0, t.length());
+                }
+            }
+        }
+        else
+        {
+            super.write(chars, off, len);
+        }
+    }
+
+    // write the string unmodified unless filtering is enabled in
+    // which case the writeFiltered(String) method is called for filtering
+    public void write(final String string, final int off, final int len)
+    {
+        write(string.toCharArray(), off, len);
+    }
+
+    private void startLine()
+    {
+        if (this.needComma)
+        {
+            super.write(',');
+            super.println();
+            this.needComma = false;
+        }
+
+        super.write("    \"".toCharArray(), 0, 5);
+        this.startLine = false;
+    }
+}
\ No newline at end of file

Added: felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/helper/PlainTextConfigurationWriter.java
URL: http://svn.apache.org/viewvc/felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/helper/PlainTextConfigurationWriter.java?rev=1486594&view=auto
==============================================================================
--- felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/helper/PlainTextConfigurationWriter.java (added)
+++ felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/helper/PlainTextConfigurationWriter.java Mon May 27 12:34:46 2013
@@ -0,0 +1,46 @@
+/*
+ * 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.felix.inventory.impl.helper;
+
+import java.io.Writer;
+
+
+/**
+ * The plain text configuration writer outputs the status as plain text.
+ */
+public class PlainTextConfigurationWriter extends ConfigurationWriter
+{
+
+    public PlainTextConfigurationWriter(final Writer delegatee)
+    {
+        super(delegatee);
+    }
+
+    protected void title(final String title)
+    {
+        print("*** ");
+        print(title);
+        println(":");
+    }
+
+    protected void end()
+    {
+        println();
+    }
+}
\ No newline at end of file

Added: felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/helper/SimpleJson.java
URL: http://svn.apache.org/viewvc/felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/helper/SimpleJson.java?rev=1486594&view=auto
==============================================================================
--- felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/helper/SimpleJson.java (added)
+++ felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/helper/SimpleJson.java Mon May 27 12:34:46 2013
@@ -0,0 +1,105 @@
+/*
+ * 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.felix.inventory.impl.helper;
+
+/**
+ * The <code>SimpleJson</code> is an extremely simple and very limited
+ * helper class to create JSON formatted output. The limits are as follows:
+ * <ol>
+ * <li>There is no error checking</li>
+ * <li>Arrays are always expected to be inside an object</li>
+ * <li>At most one level of object nesting is supported</li>
+ * <li>Strings are not escaped</li>
+ * <li>Only string values are supported</li>
+ * </ol>
+ */
+class SimpleJson
+{
+
+    private StringBuffer index = new StringBuffer();
+
+    /*
+     * "o" - object; require ";" separator
+     * "f" - object; no separator; next "o"
+     * "a" - array; require "," separator
+     * "i" - array; no separator; next "a"
+     */
+    private char mode = 0;
+
+    SimpleJson object()
+    {
+        this.index.append('{');
+        this.mode = 'f';
+        return this;
+    }
+
+    SimpleJson endObject()
+    {
+        this.index.append('}');
+        this.mode = 'o';
+        return this;
+    }
+
+    SimpleJson array()
+    {
+        this.index.append('[');
+        this.mode = 'i';
+        return this;
+    }
+
+    SimpleJson endArray()
+    {
+        this.index.append(']');
+        this.mode = 'o';
+        return this;
+    }
+
+    SimpleJson key(final String key)
+    {
+        if (this.mode == 'f')
+        {
+            this.mode = 'o';
+        }
+        else if (mode == 'o')
+        {
+            this.index.append(',');
+        }
+        this.index.append('"').append(key).append("\":");
+        return this;
+    }
+
+    SimpleJson value(final String value)
+    {
+        if (this.mode == 'i')
+        {
+            this.mode = 'a';
+        }
+        else if (mode == 'a')
+        {
+            this.index.append(',');
+        }
+        this.index.append('"').append(value).append('"');
+        return this;
+    }
+
+    public String toString()
+    {
+        return this.index.toString();
+    }
+}

Added: felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/helper/ZipConfigurationWriter.java
URL: http://svn.apache.org/viewvc/felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/helper/ZipConfigurationWriter.java?rev=1486594&view=auto
==============================================================================
--- felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/helper/ZipConfigurationWriter.java (added)
+++ felix/trunk/inventory/src/main/java/org/apache/felix/inventory/impl/helper/ZipConfigurationWriter.java Mon May 27 12:34:46 2013
@@ -0,0 +1,218 @@
+/*
+ * 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.felix.inventory.impl.helper;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.text.DateFormat;
+import java.text.MessageFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.zip.Deflater;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import org.apache.felix.inventory.Format;
+import org.apache.felix.inventory.impl.InventoryPrinterHandler;
+
+/**
+ * The ZIP configuration writer creates a zip with
+ * - txt output of a inventory printers (if supported)
+ * - json output of a inventory printers (if supported)
+ * - attachments from a inventory printer (if supported)
+ */
+public class ZipConfigurationWriter extends ConfigurationWriter
+{
+
+    private final ZipConfigurationWriter.ConfigZipOutputStream zip;
+
+    private int entryCounter;
+
+    public static ZipConfigurationWriter create(final OutputStream out) throws IOException
+    {
+        final ZipConfigurationWriter.ConfigZipOutputStream zip = new ConfigZipOutputStream(out)
+        {
+        };
+        zip.setLevel(Deflater.BEST_SPEED);
+        zip.setMethod(ZipOutputStream.DEFLATED);
+
+        return new ZipConfigurationWriter(zip);
+    }
+
+    private ZipConfigurationWriter(final ZipConfigurationWriter.ConfigZipOutputStream zip) throws IOException
+    {
+        super(new OutputStreamWriter(zip, "UTF-8"));
+
+        this.zip = zip;
+        this.entryCounter = -1;
+    }
+
+    public void finish() throws IOException
+    {
+        this.zip.finish();
+    }
+
+    /**
+     * Overwrites the
+     * {@link ConfigurationWriter#printInventory(Format, InventoryPrinterHandler)}
+     * method writing the plain text output, the JSON output and any
+     * attachements to the ZIP file. The {@code format} argument is ignored.
+     *
+     * @param formatIgnored Ignored, may be {@code null}.
+     * @param handler The handler to be called to generate the output
+     *
+     * @throws IOException if an error occurrs writing to the ZIP file.
+     */
+    public void printInventory(final Format formatIgnored, final InventoryPrinterHandler handler)
+        throws IOException
+    {
+        final String baseName = getBaseName(handler);
+
+        this.zip.handler(handler);
+
+        // print the plain text output
+        if (handler.supports(Format.TEXT))
+        {
+            final ZipEntry entry = new ZipEntry(baseName.concat(".txt"));
+            entry.setTime(System.currentTimeMillis());
+            this.zip.putNextEntry(entry, Format.TEXT);
+            handler.print(this, Format.TEXT, false);
+            this.flush();
+            this.zip.closeEntry();
+        }
+
+        // print the JSON format output
+        if (handler.supports(Format.JSON))
+        {
+            final ZipEntry entry = new ZipEntry("json/" + baseName + ".json");
+            entry.setTime(System.currentTimeMillis());
+            this.zip.putNextEntry(entry, Format.JSON);
+            handler.print(this, Format.JSON, true);
+            this.flush();
+            this.zip.closeEntry();
+        }
+
+        // any attachements from the handler
+        this.zip.attachements();
+        handler.addAttachments(this.zip, baseName.concat("/"));
+        this.zip.endAttachements();
+
+        this.zip.endHandler();
+    }
+
+    private String getBaseName(final InventoryPrinterHandler handler)
+    {
+        final String title = handler.getTitle();
+        final StringBuffer name = new StringBuffer(title.length());
+        for (int i = 0; i < title.length(); i++)
+        {
+            char c = title.charAt(i);
+            if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
+            {
+                name.append(c);
+            }
+            else
+            {
+                name.append('_');
+            }
+        }
+
+        this.entryCounter++;
+        return MessageFormat.format("{0,number,000}_{1}", new Object[]
+            { new Integer(this.entryCounter), name });
+    }
+
+    private static class ConfigZipOutputStream extends ZipOutputStream
+    {
+
+        private final SimpleJson json;
+
+        ConfigZipOutputStream(final OutputStream out)
+        {
+            super(out);
+
+            this.json = new SimpleJson();
+            this.json.object();
+
+            // timestamp in the "created" object
+            final Date now = new Date();
+            final String nowFormatted = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, Locale.US)
+                .format(now);
+            this.json.key("created");
+            this.json.object();
+            this.json.key("date").value(nowFormatted);
+            this.json.key("stamp").value(String.valueOf(now.getTime()));
+            this.json.endObject();
+
+            // output from the printers in the "files" object
+            this.json.key("files").object();
+        }
+
+        void handler(final InventoryPrinterHandler handler)
+        {
+            this.json.key(handler.getName());
+            this.json.object();
+            this.json.key("title").value(handler.getTitle());
+        }
+
+        void endHandler()
+        {
+            this.json.endObject();
+        }
+
+        void attachements()
+        {
+            this.json.key("attachements");
+            this.json.array();
+        }
+
+        void endAttachements()
+        {
+            this.json.endArray();
+        }
+
+        void putNextEntry(ZipEntry e, Format format) throws IOException
+        {
+            this.json.key(format.toString().toLowerCase());
+            this.putNextEntry(e);
+        }
+
+        public void putNextEntry(ZipEntry e) throws IOException
+        {
+            this.json.value(e.getName());
+            super.putNextEntry(e);
+        }
+
+        public void finish() throws IOException
+        {
+            // end "files" and root objects
+            this.json.endObject().endObject();
+
+            final ZipEntry entry = new ZipEntry("index.json");
+            entry.setTime(System.currentTimeMillis());
+            super.putNextEntry(entry); // don't write the index to the JSON
+            this.write(this.json.toString().getBytes("UTF-8"));
+            this.flush();
+            this.closeEntry();
+
+            super.finish();
+        }
+    }
+}
\ No newline at end of file