[FELIX-2761][FELIX-2764] Add a listener interface called when executing commands, send an osgi event if EventAdmin is present

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk/gogo/runtime@1055258 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/pom.xml b/pom.xml
index ace4cfa..667d09d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -60,6 +60,7 @@
                             org.apache.felix.service.threadio; version=${project.version}; status="provisional"; mandatory:="status"
                         </Export-Package>
                         <Import-Package>
+                            org.osgi.service.event*; resolution:=optional,
                             org.osgi.service.log*; resolution:=optional,
                             org.osgi.service.packageadmin*; resolution:=optional,
                             org.osgi.service.startlevel*; resolution:=optional,
diff --git a/src/main/java/org/apache/felix/gogo/runtime/CommandProcessorImpl.java b/src/main/java/org/apache/felix/gogo/runtime/CommandProcessorImpl.java
index 724f189..6cbb70b 100644
--- a/src/main/java/org/apache/felix/gogo/runtime/CommandProcessorImpl.java
+++ b/src/main/java/org/apache/felix/gogo/runtime/CommandProcessorImpl.java
@@ -21,10 +21,18 @@
 import java.io.InputStream;
 import java.io.PrintStream;
 import java.lang.reflect.Method;
-import java.util.*;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.WeakHashMap;
+import java.util.concurrent.CopyOnWriteArraySet;
 
-import org.osgi.framework.BundleContext;
+import org.apache.felix.service.command.CommandSessionListener;
 import org.apache.felix.service.command.CommandProcessor;
 import org.apache.felix.service.command.CommandSession;
 import org.apache.felix.service.command.Converter;
@@ -34,6 +42,7 @@
 public class CommandProcessorImpl implements CommandProcessor
 {
     protected final Set<Converter> converters = new HashSet<Converter>();
+    protected final Set<CommandSessionListener> listeners = new CopyOnWriteArraySet<CommandSessionListener>();
     protected final Map<String, Object> commands = new LinkedHashMap<String, Object>();
     protected final Map<String, Object> constants = new HashMap<String, Object>();
     protected final ThreadIO threadIO;
@@ -69,6 +78,17 @@
         converters.remove(c);
     }
 
+    public void addListener(CommandSessionListener l)
+    {
+        listeners.add(l);
+    }
+
+    public void removeListener(CommandSessionListener l)
+    {
+        listeners.remove(l);
+    }
+
+
     public Set<String> getCommands()
     {
         return commands.keySet();
@@ -243,4 +263,50 @@
 
         return session.execute(buf);
     }
+
+    void beforeExecute(CommandSession session, CharSequence commandline)
+    {
+        for (CommandSessionListener l : listeners)
+        {
+            try
+            {
+                l.beforeExecute(session, commandline);
+            }
+            catch (Throwable t)
+            {
+                // Ignore
+            }
+        }
+    }
+
+    void afterExecute(CommandSession session, CharSequence commandline, Exception exception)
+    {
+        for (CommandSessionListener l : listeners)
+        {
+            try
+            {
+                l.afterExecute(session, commandline, exception);
+            }
+            catch (Throwable t)
+            {
+                // Ignore
+            }
+        }
+    }
+
+    void afterExecute(CommandSession session, CharSequence commandline, Object result)
+    {
+        for (CommandSessionListener l : listeners)
+        {
+            try
+            {
+                l.afterExecute(session, commandline, result);
+            }
+            catch (Throwable t)
+            {
+                // Ignore
+            }
+        }
+    }
+
 }
diff --git a/src/main/java/org/apache/felix/gogo/runtime/CommandSessionImpl.java b/src/main/java/org/apache/felix/gogo/runtime/CommandSessionImpl.java
index 42d7ee1..447e735 100644
--- a/src/main/java/org/apache/felix/gogo/runtime/CommandSessionImpl.java
+++ b/src/main/java/org/apache/felix/gogo/runtime/CommandSessionImpl.java
@@ -81,18 +81,18 @@
             throw new IllegalStateException(SESSION_CLOSED);
         }
 
-        beforeExecute(commandline);
+        processor.beforeExecute(this, commandline);
 
         try
         {
             Closure impl = new Closure(this, null, commandline);
             Object result = impl.execute(this, null);
-            afterExecute(commandline, result);
+            processor.afterExecute(this, commandline, result);
             return result;
         }
         catch (Exception e)
         {
-            afterExecute(commandline, e);
+            processor.afterExecute(this, commandline, e);
             throw e;
         }
     }
@@ -386,19 +386,4 @@
         }
     }
 
-    protected void beforeExecute(CharSequence commandline)
-    {
-        // Centralized callback for derived implementation
-    }
-
-    protected void afterExecute(CharSequence commandline, Exception exception)
-    {
-        // Centralized callback for derived implementation
-    }
-
-    protected void afterExecute(CharSequence commandline, Object result)
-    {
-        // Centralized callback for derived implementation
-    }
-
 }
diff --git a/src/main/java/org/apache/felix/gogo/runtime/activator/Activator.java b/src/main/java/org/apache/felix/gogo/runtime/activator/Activator.java
index d42a898..b60ed65 100644
--- a/src/main/java/org/apache/felix/gogo/runtime/activator/Activator.java
+++ b/src/main/java/org/apache/felix/gogo/runtime/activator/Activator.java
@@ -23,6 +23,7 @@
 
 import org.apache.felix.gogo.runtime.CommandProcessorImpl;
 import org.apache.felix.gogo.runtime.CommandProxy;
+import org.apache.felix.service.command.CommandSessionListener;
 import org.apache.felix.gogo.runtime.threadio.ThreadIOImpl;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
@@ -42,6 +43,7 @@
     private ThreadIOImpl threadio;
     private ServiceTracker commandTracker;
     private ServiceTracker converterTracker;
+    private ServiceTracker listenerTracker;
     private ServiceRegistration processorRegistration;
     private ServiceRegistration threadioRegistration;
     
@@ -50,6 +52,14 @@
     protected ServiceRegistration newProcessor(ThreadIO tio, BundleContext context)
     {
         processor = new CommandProcessorImpl(tio);
+        try
+        {
+            processor.addListener(new EventAdminListener(context));
+        }
+        catch (NoClassDefFoundError error)
+        {
+            // Ignore the listener if EventAdmin package isn't present
+        }
 
         // Setup the variables and commands exposed in an OSGi environment.
         processor.addConstant(CONTEXT, context);
@@ -90,6 +100,23 @@
             }
         };
         converterTracker.open();
+
+        listenerTracker = new ServiceTracker(context, CommandSessionListener.class.getName(), null)
+        {
+            @Override
+            public Object addingService(ServiceReference reference) {
+                CommandSessionListener listener = (CommandSessionListener) super.addingService(reference);
+                processor.addListener(listener);
+                return listener;
+            }
+
+            @Override
+            public void removedService(ServiceReference reference, Object service) {
+                processor.removeListener((CommandSessionListener) service);
+                super.removedService(reference, service);
+            }
+        };
+        listenerTracker.open();
     }
 
     public void stop(BundleContext context) throws Exception
@@ -98,6 +125,7 @@
         threadioRegistration.unregister();
         commandTracker.close();
         converterTracker.close();
+        listenerTracker.close();
         threadio.stop();
         processor.stop();
     }
diff --git a/src/main/java/org/apache/felix/gogo/runtime/activator/EventAdminListener.java b/src/main/java/org/apache/felix/gogo/runtime/activator/EventAdminListener.java
new file mode 100644
index 0000000..2ac94d0
--- /dev/null
+++ b/src/main/java/org/apache/felix/gogo/runtime/activator/EventAdminListener.java
@@ -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.felix.gogo.runtime.activator;
+
+import java.util.Properties;
+
+import org.apache.felix.service.command.CommandSession;
+import org.apache.felix.service.command.CommandSessionListener;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+import org.osgi.util.tracker.ServiceTracker;
+
+public class EventAdminListener implements CommandSessionListener
+{
+
+    private BundleContext bundleContext;
+    private ServiceTracker tracker;
+
+    public EventAdminListener(BundleContext bundleContext)
+    {
+        this.bundleContext = bundleContext;
+        tracker = new ServiceTracker(bundleContext, EventAdmin.class.getName(), null);
+        tracker.open();
+    }
+
+    public void beforeExecute(CommandSession session, CharSequence command) {
+        EventAdmin admin = (EventAdmin) tracker.getService();
+        if (admin != null) {
+            Properties props = new Properties();
+            props.setProperty("command", command.toString());
+            Event event = new Event("org/apache/felix/service/command/EXECUTING", props);
+            admin.postEvent(event);
+        }
+    }
+
+    public void afterExecute(CommandSession session, CharSequence command, Exception exception) {
+    }
+
+    public void afterExecute(CommandSession session, CharSequence command, Object result) {
+    }
+
+}
diff --git a/src/main/java/org/apache/felix/service/command/CommandSessionListener.java b/src/main/java/org/apache/felix/service/command/CommandSessionListener.java
new file mode 100644
index 0000000..30e9b21
--- /dev/null
+++ b/src/main/java/org/apache/felix/service/command/CommandSessionListener.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.service.command;
+
+/**
+ * Listener for command executions.
+ *
+ * Such listeners must be registered in the OSGi registry and will be called
+ * by the CommandProcessor when a command line is executed in a given session.
+ */
+public interface CommandSessionListener {
+
+    void beforeExecute(CommandSession session, CharSequence command);
+
+    void afterExecute(CommandSession session, CharSequence command, Exception exception);
+
+    void afterExecute(CommandSession session, CharSequence command, Object result);
+
+}