Applied patch (FELIX-1052) to add "log" command to shell.


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@769156 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/shell/pom.xml b/shell/pom.xml
index 92ffc0a..2fc6abf 100644
--- a/shell/pom.xml
+++ b/shell/pom.xml
@@ -35,6 +35,11 @@
       <artifactId>org.osgi.core</artifactId>
       <version>1.2.0</version>
     </dependency>
+    <dependency>
+      <groupId>${pom.groupId}</groupId>
+      <artifactId>org.osgi.compendium</artifactId>
+      <version>1.2.0</version>
+    </dependency>    
   </dependencies>
   <build>
     <plugins>
@@ -45,7 +50,7 @@
         <extensions>true</extensions>
         <configuration>
           <instructions>
-            <Export-Package>org.apache.felix.shell; org.ungoverned.osgi.service.shell; version=1.0.0</Export-Package>
+            <Export-Package>org.osgi.service.log, org.apache.felix.shell; org.ungoverned.osgi.service.shell; version=1.0.0</Export-Package>
             <Private-Package>org.apache.felix.*</Private-Package>
             <Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
             <Bundle-Activator>org.apache.felix.shell.impl.Activator</Bundle-Activator>
diff --git a/shell/src/main/java/org/apache/felix/shell/impl/Activator.java b/shell/src/main/java/org/apache/felix/shell/impl/Activator.java
index 8940ad6..92ff81a 100644
--- a/shell/src/main/java/org/apache/felix/shell/impl/Activator.java
+++ b/shell/src/main/java/org/apache/felix/shell/impl/Activator.java
@@ -122,6 +122,11 @@
             org.apache.felix.shell.Command.class.getName(),
             new InstallCommandImpl(m_context), null);
 
+        // Register "log" command service.
+        context.registerService(
+            org.apache.felix.shell.Command.class.getName(),
+            new LogCommandImpl(m_context), null);
+
         // Register "ps" command service.
         context.registerService(
             org.apache.felix.shell.Command.class.getName(),
diff --git a/shell/src/main/java/org/apache/felix/shell/impl/LogCommandImpl.java b/shell/src/main/java/org/apache/felix/shell/impl/LogCommandImpl.java
new file mode 100644
index 0000000..dca09e8
--- /dev/null
+++ b/shell/src/main/java/org/apache/felix/shell/impl/LogCommandImpl.java
@@ -0,0 +1,124 @@
+/*
+ * 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.shell.impl;
+
+import java.io.PrintStream;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Enumeration;
+
+import org.apache.felix.shell.Command;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.log.LogEntry;
+import org.osgi.service.log.LogReaderService;
+
+/**
+ * Apache Felix Shell command to display recent log entries
+ */
+public class LogCommandImpl implements Command
+{
+    private final BundleContext m_bundleContext;
+
+    public LogCommandImpl(BundleContext context)
+    {
+        m_bundleContext = context;
+    }
+
+    public void execute(String line, PrintStream out, PrintStream err)
+    {
+        LogOptions options = new LogOptions(line);
+
+        ServiceReference ref =
+            m_bundleContext.getServiceReference(LogReaderService.class.getName());
+        if (ref != null)
+        {
+            LogReaderService service = (LogReaderService) m_bundleContext.getService(ref);
+            Enumeration entries = service.getLog();
+
+            int index = 0;
+            while (entries.hasMoreElements()
+                && (options.getMaxNumberOfLogs() < 0 | index < options.getMaxNumberOfLogs()))
+            {
+                LogEntry entry = (LogEntry) entries.nextElement();
+                if (entry.getLevel() <= options.getMinLevel())
+                {
+                    display(entry, out);
+                    index++;
+                }
+            }
+        }
+        else
+        {
+            out.println("No LogReaderService available");
+        }
+    }
+
+    private void display(LogEntry entry, PrintStream out)
+    {
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
+
+        StringBuffer buffer = new StringBuffer();
+        buffer.append(sdf.format(new Date(entry.getTime()))).append(" ");
+        buffer.append(levelAsAString(entry.getLevel())).append(" - ");
+        buffer.append("Bundle:").append(entry.getBundle().getSymbolicName()).append(" ");
+        if (entry.getServiceReference() != null)
+        {
+            buffer.append(entry.getServiceReference().toString()).append(" ");
+        }
+        buffer.append("- ").append(entry.getMessage()).append(" - ");
+        if (entry.getException() != null)
+        {
+            buffer.append(entry.getException().getClass().getName()).append(": ").append(
+                entry.getException().getMessage());
+        }
+
+        out.println(buffer.toString());
+    }
+
+    private String levelAsAString(int level)
+    {
+        switch (level)
+        {
+            case 1:
+                return "ERROR";
+            case 2:
+                return "WARNING";
+            case 3:
+                return "INFO";
+            default:
+                return "DEBUG";
+        }
+    }
+
+    public String getName()
+    {
+        return "log";
+    }
+
+    public String getShortDescription()
+    {
+        return "list the most recent log entries";
+    }
+
+    public String getUsage()
+    {
+        return "log [<max log entries>] [error | warn | info | debug]";
+    }
+}
\ No newline at end of file
diff --git a/shell/src/main/java/org/apache/felix/shell/impl/LogOptions.java b/shell/src/main/java/org/apache/felix/shell/impl/LogOptions.java
new file mode 100644
index 0000000..1106570
--- /dev/null
+++ b/shell/src/main/java/org/apache/felix/shell/impl/LogOptions.java
@@ -0,0 +1,86 @@
+/*
+ * 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.shell.impl;
+
+import java.util.StringTokenizer;
+
+/**
+ * Parse and encapsulate command line options
+ *
+ */
+public class LogOptions
+{
+    private int minLevel = 4;
+    private int maxNumberOfLogs = -1;
+
+    public LogOptions(String commandLine)
+    {
+        StringTokenizer st = new StringTokenizer(commandLine);
+        readOptions(st);
+    }
+
+    private void readOptions(StringTokenizer st)
+    {
+        if (st.countTokens() > 1)
+        {
+            st.nextToken();
+            String firstOption = st.nextToken();
+            checkOption(firstOption);
+
+            if (st.hasMoreTokens())
+            {
+                checkOption(st.nextToken());
+            }
+        }
+    }
+
+    private void checkOption(String opt)
+    {
+        try
+        {
+            maxNumberOfLogs = Integer.parseInt(opt);
+        }
+        catch (NumberFormatException nfe)
+        {
+            //do nothing, it's not a number
+        }
+        if ("info".equalsIgnoreCase(opt))
+        {
+            minLevel = 3;
+        }
+        else if ("warn".equalsIgnoreCase(opt))
+        {
+            minLevel = 2;
+        }
+        else if ("error".equalsIgnoreCase(opt))
+        {
+            minLevel = 1;
+        }
+    }
+
+    public int getMinLevel()
+    {
+        return minLevel;
+    }
+
+    public int getMaxNumberOfLogs()
+    {
+        return maxNumberOfLogs;
+    }
+}
\ No newline at end of file
diff --git a/shell/src/test/java/org/apache/felix/shell/impl/LogOptionsTest.java b/shell/src/test/java/org/apache/felix/shell/impl/LogOptionsTest.java
new file mode 100644
index 0000000..9a26083
--- /dev/null
+++ b/shell/src/test/java/org/apache/felix/shell/impl/LogOptionsTest.java
@@ -0,0 +1,130 @@
+/*
+ * 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.shell.impl;
+
+import junit.framework.TestCase;
+
+public class LogOptionsTest extends TestCase
+{
+    public void testOnlyLogCommand()
+    {
+        LogOptions opt = new LogOptions("log");
+        assertEquals(4, opt.getMinLevel());
+        assertEquals(-1, opt.getMaxNumberOfLogs());
+
+        opt = new LogOptions("log  ");
+        assertEquals(4, opt.getMinLevel());
+        assertEquals(-1, opt.getMaxNumberOfLogs());
+    }
+
+    public void testLogWithMinLevelDebug()
+    {
+        LogOptions opt = new LogOptions("log debug");
+        assertEquals(4, opt.getMinLevel());
+        assertEquals(-1, opt.getMaxNumberOfLogs());
+
+        opt = new LogOptions("log DEBUG");
+        assertEquals(4, opt.getMinLevel());
+        assertEquals(-1, opt.getMaxNumberOfLogs());
+    }
+
+    public void testLogWithMinLevelInfo()
+    {
+        LogOptions opt = new LogOptions("log info");
+        assertEquals(3, opt.getMinLevel());
+        assertEquals(-1, opt.getMaxNumberOfLogs());
+
+        opt = new LogOptions("log INFO");
+        assertEquals(3, opt.getMinLevel());
+        assertEquals(-1, opt.getMaxNumberOfLogs());
+    }
+
+    public void testLogWithMinLevelWarn()
+    {
+        LogOptions opt = new LogOptions("log warn");
+        assertEquals(2, opt.getMinLevel());
+        assertEquals(-1, opt.getMaxNumberOfLogs());
+
+        opt = new LogOptions("log WARN");
+        assertEquals(2, opt.getMinLevel());
+        assertEquals(-1, opt.getMaxNumberOfLogs());
+    }
+
+    public void testLogWithMinLevelError()
+    {
+        LogOptions opt = new LogOptions("log error");
+        assertEquals(1, opt.getMinLevel());
+        assertEquals(-1, opt.getMaxNumberOfLogs());
+
+        opt = new LogOptions("log ERROR");
+        assertEquals(1, opt.getMinLevel());
+        assertEquals(-1, opt.getMaxNumberOfLogs());
+    }
+
+    public void testLogWithMaxNumberOfLogs()
+    {
+        LogOptions opt = new LogOptions("log 42");
+        assertEquals(4, opt.getMinLevel());
+        assertEquals(42, opt.getMaxNumberOfLogs());
+    }
+
+    public void testLogWithMinLevelDebugAndMaxNumberOfLogs()
+    {
+        LogOptions opt = new LogOptions("log debug 12");
+        assertEquals(4, opt.getMinLevel());
+        assertEquals(12, opt.getMaxNumberOfLogs());
+
+        opt = new LogOptions("log 13 DEBUG");
+        assertEquals(4, opt.getMinLevel());
+        assertEquals(13, opt.getMaxNumberOfLogs());
+    }
+
+    public void testLogWithMinLevelInfoAndMaxNumberOfLogs()
+    {
+        LogOptions opt = new LogOptions("log info 14");
+        assertEquals(3, opt.getMinLevel());
+        assertEquals(14, opt.getMaxNumberOfLogs());
+
+        opt = new LogOptions("log 15 INFO");
+        assertEquals(3, opt.getMinLevel());
+        assertEquals(15, opt.getMaxNumberOfLogs());
+    }
+
+    public void testLogWithMinLevelWarnAndMaxNumberOfLogs()
+    {
+        LogOptions opt = new LogOptions("log warn 16");
+        assertEquals(2, opt.getMinLevel());
+        assertEquals(16, opt.getMaxNumberOfLogs());
+
+        opt = new LogOptions("log 17 WARN");
+        assertEquals(2, opt.getMinLevel());
+        assertEquals(17, opt.getMaxNumberOfLogs());
+    }
+
+    public void testLogWithMinLevelErrorAndMaxNumberOfLogs()
+    {
+        LogOptions opt = new LogOptions("log error 18");
+        assertEquals(1, opt.getMinLevel());
+        assertEquals(18, opt.getMaxNumberOfLogs());
+
+        opt = new LogOptions("log 19 ERROR");
+        assertEquals(1, opt.getMinLevel());
+        assertEquals(19, opt.getMaxNumberOfLogs());
+    }
+}
\ No newline at end of file