You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by cz...@apache.org on 2013/12/30 11:39:02 UTC

svn commit: r1554180 - in /felix/trunk/coordinator: ./ src/main/java/org/apache/felix/coordinator/impl/

Author: cziegeler
Date: Mon Dec 30 10:39:01 2013
New Revision: 1554180

URL: http://svn.apache.org/r1554180
Log:
FELIX-2647 : Implement Coordinator Service - Add basic logging and security checks

Added:
    felix/trunk/coordinator/src/main/java/org/apache/felix/coordinator/impl/LogWrapper.java   (with props)
Modified:
    felix/trunk/coordinator/pom.xml
    felix/trunk/coordinator/src/main/java/org/apache/felix/coordinator/impl/Activator.java
    felix/trunk/coordinator/src/main/java/org/apache/felix/coordinator/impl/CoordinationImpl.java
    felix/trunk/coordinator/src/main/java/org/apache/felix/coordinator/impl/CoordinatorImpl.java

Modified: felix/trunk/coordinator/pom.xml
URL: http://svn.apache.org/viewvc/felix/trunk/coordinator/pom.xml?rev=1554180&r1=1554179&r2=1554180&view=diff
==============================================================================
--- felix/trunk/coordinator/pom.xml (original)
+++ felix/trunk/coordinator/pom.xml Mon Dec 30 10:39:01 2013
@@ -57,6 +57,16 @@
                         <Bundle-DocURL>
                             http://felix.apache.org/site/apache-felix-coordination-service.html
                         </Bundle-DocURL>
+                        <DynamicImport-Package>
+                            org.osgi.service.log;version="[1.3,2)"
+                        </DynamicImport-Package>
+                        <Import-Package>
+                            <!--
+                                Optional import to back the dynamic import on org.osgi.service.log
+                            -->
+                            org.osgi.service.log;version="[1.3,2)";resolution:=optional,
+                            *
+                        </Import-Package>
                         <Export-Package>
                             org.osgi.service.coordinator
                         </Export-Package>
@@ -77,11 +87,13 @@
             <groupId>org.osgi</groupId>
             <artifactId>org.osgi.core</artifactId>
             <version>4.3.0</version>
+            <scope>provided</scope>
         </dependency>
         <dependency>
             <groupId>org.osgi</groupId>
-            <artifactId>org.osgi.enterprise</artifactId>
+            <artifactId>org.osgi.compendium</artifactId>
             <version>5.0.0</version>
+            <scope>provided</scope>
         </dependency>
     </dependencies>
 </project>

Modified: felix/trunk/coordinator/src/main/java/org/apache/felix/coordinator/impl/Activator.java
URL: http://svn.apache.org/viewvc/felix/trunk/coordinator/src/main/java/org/apache/felix/coordinator/impl/Activator.java?rev=1554180&r1=1554179&r2=1554180&view=diff
==============================================================================
--- felix/trunk/coordinator/src/main/java/org/apache/felix/coordinator/impl/Activator.java (original)
+++ felix/trunk/coordinator/src/main/java/org/apache/felix/coordinator/impl/Activator.java Mon Dec 30 10:39:01 2013
@@ -36,8 +36,10 @@ public class Activator implements Bundle
 
     private ServiceRegistration coordinatorService;
 
-    public void start(BundleContext context)
+    public void start(final BundleContext context)
     {
+        LogWrapper.setContext(context);
+
         mgr = new CoordinationMgr();
 
         final ServiceFactory factory = new CoordinatorFactory(mgr);
@@ -47,7 +49,7 @@ public class Activator implements Bundle
         coordinatorService = context.registerService(Coordinator.class.getName(), factory, props);
     }
 
-    public void stop(BundleContext context)
+    public void stop(final BundleContext context)
     {
         if (coordinatorService != null)
         {
@@ -56,6 +58,8 @@ public class Activator implements Bundle
         }
 
         mgr.cleanUp();
+
+        LogWrapper.setContext(null);
     }
 
     static final class CoordinatorFactory implements ServiceFactory
@@ -68,12 +72,12 @@ public class Activator implements Bundle
             this.mgr = mgr;
         }
 
-        public Object getService(Bundle bundle, ServiceRegistration registration)
+        public Object getService(final Bundle bundle, final ServiceRegistration registration)
         {
             return new CoordinatorImpl(bundle, mgr);
         }
 
-        public void ungetService(Bundle bundle, ServiceRegistration registration, Object service)
+        public void ungetService(final Bundle bundle, final ServiceRegistration registration, final Object service)
         {
             ((CoordinatorImpl) service).dispose();
         }

Modified: felix/trunk/coordinator/src/main/java/org/apache/felix/coordinator/impl/CoordinationImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/coordinator/src/main/java/org/apache/felix/coordinator/impl/CoordinationImpl.java?rev=1554180&r1=1554179&r2=1554180&view=diff
==============================================================================
--- felix/trunk/coordinator/src/main/java/org/apache/felix/coordinator/impl/CoordinationImpl.java (original)
+++ felix/trunk/coordinator/src/main/java/org/apache/felix/coordinator/impl/CoordinationImpl.java Mon Dec 30 10:39:01 2013
@@ -30,6 +30,7 @@ import java.util.TimerTask;
 import org.osgi.framework.Bundle;
 import org.osgi.service.coordinator.Coordination;
 import org.osgi.service.coordinator.CoordinationException;
+import org.osgi.service.coordinator.CoordinationPermission;
 import org.osgi.service.coordinator.Participant;
 
 public class CoordinationImpl implements Coordination
@@ -57,7 +58,6 @@ public class CoordinationImpl implements
 
     private final String name;
 
-    // TODO: timeout must be enforced
     private long deadLine;
 
     /**
@@ -116,6 +116,7 @@ public class CoordinationImpl implements
      */
     public boolean fail(final Throwable reason)
     {
+        this.owner.checkPermission(name, CoordinationPermission.PARTICIPATE);
         if ( reason == null)
         {
             throw new IllegalArgumentException("Reason must not be null");
@@ -138,9 +139,10 @@ public class CoordinationImpl implements
                 {
                     part.failed(this);
                 }
-                catch (Exception e)
+                catch (final Exception e)
                 {
-                    // TODO: log
+                    LogWrapper.getLogger()
+                        .log(LogWrapper.LOG_ERROR, "Participant threw exception during call to fail()", e);
                 }
 
                 // release the participant for other coordinations
@@ -165,6 +167,7 @@ public class CoordinationImpl implements
      */
     public void end()
     {
+        this.owner.checkPermission(name, CoordinationPermission.INITIATE);
         if ( !this.isTerminated() && this.associatedThread != null && Thread.currentThread() != this.associatedThread )
         {
             throw new CoordinationException("Coordination is associated with different thread", this, CoordinationException.WRONG_THREAD);
@@ -204,7 +207,8 @@ public class CoordinationImpl implements
                 }
                 catch (final Exception e)
                 {
-                    // TODO: log
+                    LogWrapper.getLogger()
+                        .log(LogWrapper.LOG_ERROR, "Participant threw exception during call to fail()", e);
                     partialFailure = true;
                 }
 
@@ -252,6 +256,7 @@ public class CoordinationImpl implements
      */
     public List<Participant> getParticipants()
     {
+        this.owner.checkPermission(name, CoordinationPermission.INITIATE);
         // synchronize access to the state to prevent it from being changed
         // while we create a copy of the participant list
         synchronized (this)
@@ -273,6 +278,7 @@ public class CoordinationImpl implements
      */
     public Throwable getFailure()
     {
+        this.owner.checkPermission(name, CoordinationPermission.INITIATE);
         return failReason;
     }
 
@@ -282,6 +288,7 @@ public class CoordinationImpl implements
      */
     public void addParticipant(final Participant p)
     {
+        this.owner.checkPermission(name, CoordinationPermission.PARTICIPATE);
         if ( p == null ) {
             throw new IllegalArgumentException("Participant must not be null");
         }
@@ -327,6 +334,7 @@ public class CoordinationImpl implements
      */
     public Map<Class<?>, Object> getVariables()
     {
+        this.owner.checkPermission(name, CoordinationPermission.PARTICIPATE);
         return variables;
     }
 
@@ -335,6 +343,7 @@ public class CoordinationImpl implements
      */
     public long extendTimeout(final long timeOutInMs)
     {
+        this.owner.checkPermission(name, CoordinationPermission.PARTICIPATE);
         if ( timeOutInMs < 0 )
         {
             throw new IllegalArgumentException("Timeout must not be negative");
@@ -373,6 +382,7 @@ public class CoordinationImpl implements
      */
     public Thread getThread()
     {
+        this.owner.checkPermission(name, CoordinationPermission.ADMIN);
         return associatedThread;
     }
 
@@ -381,6 +391,7 @@ public class CoordinationImpl implements
      */
     public void join(final long timeOutInMs) throws InterruptedException
     {
+        this.owner.checkPermission(name, CoordinationPermission.PARTICIPATE);
         if ( timeOutInMs < 0 )
         {
             throw new IllegalArgumentException("Timeout must not be negative");
@@ -400,6 +411,7 @@ public class CoordinationImpl implements
      */
     public Coordination push()
     {
+        this.owner.checkPermission(name, CoordinationPermission.INITIATE);
     	if ( isTerminated() )
     	{
             throw new CoordinationException("Coordination already ended", this, CoordinationException.ALREADY_ENDED);
@@ -414,6 +426,7 @@ public class CoordinationImpl implements
      */
     public Bundle getBundle()
     {
+        this.owner.checkPermission(name, CoordinationPermission.ADMIN);
         return this.owner.getBundle();
     }
 
@@ -422,6 +435,7 @@ public class CoordinationImpl implements
      */
     public Coordination getEnclosingCoordination()
     {
+        this.owner.checkPermission(name, CoordinationPermission.ADMIN);
         Coordination c = this.owner.getEnclosingCoordination(this);
         if ( c != null )
         {

Modified: felix/trunk/coordinator/src/main/java/org/apache/felix/coordinator/impl/CoordinatorImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/coordinator/src/main/java/org/apache/felix/coordinator/impl/CoordinatorImpl.java?rev=1554180&r1=1554179&r2=1554180&view=diff
==============================================================================
--- felix/trunk/coordinator/src/main/java/org/apache/felix/coordinator/impl/CoordinatorImpl.java (original)
+++ felix/trunk/coordinator/src/main/java/org/apache/felix/coordinator/impl/CoordinatorImpl.java Mon Dec 30 10:39:01 2013
@@ -20,6 +20,7 @@ package org.apache.felix.coordinator.imp
 
 import java.security.Permission;
 import java.util.Collection;
+import java.util.Iterator;
 import java.util.TimerTask;
 
 import org.osgi.framework.Bundle;
@@ -125,7 +126,7 @@ public class CoordinatorImpl implements 
         }
     }
 
-    private void checkPermission(final String coordinationName, final String actions )
+    public void checkPermission(final String coordinationName, final String actions )
     {
         final SecurityManager securityManager = System.getSecurityManager();
         if (securityManager != null)
@@ -160,8 +161,20 @@ public class CoordinatorImpl implements 
      */
     public Collection<Coordination> getCoordinations()
     {
-        // TODO: check permission
-        return mgr.getCoordinations();
+        final Collection<Coordination> result = mgr.getCoordinations();
+        final Iterator<Coordination> i = result.iterator();
+        while ( i.hasNext() )
+        {
+            final Coordination c = i.next();
+            try {
+                this.checkPermission(c.getName(), CoordinationPermission.ADMIN);
+            }
+            catch (final SecurityException se)
+            {
+                i.remove();
+            }
+        }
+        return result;
     }
 
     /**
@@ -169,7 +182,6 @@ public class CoordinatorImpl implements 
      */
     public boolean fail(final Throwable reason)
     {
-        // TODO: check permission
         CoordinationImpl current = (CoordinationImpl)mgr.peek();
         if (current != null)
         {
@@ -183,7 +195,6 @@ public class CoordinatorImpl implements 
      */
     public Coordination peek()
     {
-        // TODO: check permission
         Coordination c = mgr.peek();
         if ( c != null )
         {
@@ -217,10 +228,10 @@ public class CoordinatorImpl implements 
      */
     public Coordination pop()
     {
-        // TODO: check permission
         Coordination c = mgr.pop();
         if ( c != null )
         {
+            checkPermission(c.getName(), CoordinationPermission.INITIATE);
             c = ((CoordinationImpl)c).getHolder();
         }
         return c;
@@ -231,7 +242,6 @@ public class CoordinatorImpl implements 
      */
     public boolean addParticipant(final Participant participant)
     {
-        // TODO: check permission
         Coordination current = peek();
         if (current != null)
         {
@@ -246,11 +256,15 @@ public class CoordinatorImpl implements 
      */
     public Coordination getCoordination(final long id)
     {
-        // TODO: check permission
         Coordination c = mgr.getCoordinationById(id);
         if ( c != null )
         {
-            c = ((CoordinationImpl)c).getHolder();
+            try {
+                checkPermission(c.getName(), CoordinationPermission.ADMIN);
+                c = ((CoordinationImpl)c).getHolder();
+            } catch (final SecurityException e) {
+                c = null;
+            }
         }
         return c;
     }

Added: felix/trunk/coordinator/src/main/java/org/apache/felix/coordinator/impl/LogWrapper.java
URL: http://svn.apache.org/viewvc/felix/trunk/coordinator/src/main/java/org/apache/felix/coordinator/impl/LogWrapper.java?rev=1554180&view=auto
==============================================================================
--- felix/trunk/coordinator/src/main/java/org/apache/felix/coordinator/impl/LogWrapper.java (added)
+++ felix/trunk/coordinator/src/main/java/org/apache/felix/coordinator/impl/LogWrapper.java Mon Dec 30 10:39:01 2013
@@ -0,0 +1,391 @@
+/*
+ * 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.coordinator.impl;
+
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * This class mimics the standard OSGi <tt>LogService</tt> interface. It logs to an
+ * available log service with the highest service ranking.
+ *
+ * @see org.osgi.service.log.LogService
+**/
+public class LogWrapper
+{
+    /**
+     * ERROR LEVEL
+     *
+     * @see org.osgi.service.log.LogService#LOG_ERROR
+     */
+    public static final int LOG_ERROR = 1;
+
+    /**
+     * WARNING LEVEL
+     *
+     * @see org.osgi.service.log.LogService#LOG_WARNING
+     */
+    public static final int LOG_WARNING = 2;
+
+    /**
+     * INFO LEVEL
+     *
+     * @see org.osgi.service.log.LogService#LOG_INFO
+     */
+    public static final int LOG_INFO = 3;
+
+    /**
+     * DEBUG LEVEL
+     *
+     * @see org.osgi.service.log.LogService#LOG_DEBUG
+     */
+    public static final int LOG_DEBUG = 4;
+
+    /** A sorted set containing the currently available LogServices.
+     * Furthermore used as lock
+     */
+    private final Set<ServiceReference> loggerRefs = new TreeSet<ServiceReference>(
+            new Comparator<ServiceReference>() {
+
+                public int compare(ServiceReference o1, ServiceReference o2) {
+                    return o2.compareTo(o1);
+                }
+
+            });
+
+    /**
+     *  Only null while not set and loggerRefs is empty hence, only needs to be
+     *  checked in case m_loggerRefs is empty otherwise it will not be null.
+     */
+    private BundleContext context;
+
+    private ServiceListener logServiceListener;
+
+    /**
+     * Current log level. Message with log level less than or equal to
+     * current log level will be logged.
+     * The default value is {@link #LOG_WARNING}
+     *
+     * @see #setLogLevel(int)
+     */
+    private int logLevel = LOG_WARNING;
+
+    /**
+     * Create the singleton
+     */
+    private static class LogWrapperLoader
+    {
+        static final LogWrapper SINGLETON = new LogWrapper();
+    }
+
+    /**
+     * Returns the singleton instance of this LogWrapper that can be used to send
+     * log messages to all currently available LogServices or to standard output,
+     * respectively.
+     *
+     * @return the singleton instance of this LogWrapper.
+     */
+    public static LogWrapper getLogger()
+    {
+        return LogWrapperLoader.SINGLETON;
+    }
+
+    /**
+     * Set the <tt>BundleContext</tt> of the bundle. This method registers a service
+     * listener for LogServices with the framework that are subsequently used to
+     * log messages.
+     * <p>
+     * If the bundle context is <code>null</code>, the service listener is
+     * unregistered and all remaining references to LogServices dropped before
+     * internally clearing the bundle context field.
+     *
+     *  @param context The context of the bundle.
+     */
+    public static void setContext( final BundleContext context )
+    {
+        final LogWrapper logWrapper = LogWrapperLoader.SINGLETON;
+
+        // context is removed, unregister and drop references
+        if ( context == null )
+        {
+            if ( logWrapper.logServiceListener != null )
+            {
+                logWrapper.context.removeServiceListener( logWrapper.logServiceListener );
+                logWrapper.logServiceListener = null;
+            }
+            logWrapper.removeLoggerRefs();
+        }
+
+        // set field
+        logWrapper.setBundleContext( context );
+
+        // context is set, register and get existing services
+        if ( context != null )
+        {
+            try
+            {
+                final ServiceListener listener = new ServiceListener()
+                {
+                    // Add a newly available LogService reference to the singleton.
+                    public void serviceChanged( final ServiceEvent event )
+                    {
+                        if ( ServiceEvent.REGISTERED == event.getType() )
+                        {
+                            LogWrapperLoader.SINGLETON.addLoggerRef( event.getServiceReference() );
+                        }
+                        else if ( ServiceEvent.UNREGISTERING == event.getType() )
+                        {
+                            LogWrapperLoader.SINGLETON.removeLoggerRef( event.getServiceReference() );
+                        }
+                    }
+
+                };
+                context.addServiceListener( listener, "(" + Constants.OBJECTCLASS + "=org.osgi.service.log.LogService)" );
+                logWrapper.logServiceListener = listener;
+
+                // Add all available LogService references to the singleton.
+                final ServiceReference[] refs = context.getServiceReferences( "org.osgi.service.log.LogService", null );
+
+                if ( null != refs )
+                {
+                    for ( int i = 0; i < refs.length; i++ )
+                    {
+                        logWrapper.addLoggerRef( refs[i] );
+                    }
+                }
+            }
+            catch ( InvalidSyntaxException e )
+            {
+                // this never happens
+            }
+        }
+    }
+
+
+    /**
+     * The private singleton constructor.
+     */
+    LogWrapper()
+    {
+        // Singleton
+    }
+
+    /**
+     * Removes all references to LogServices still kept
+     */
+    void removeLoggerRefs()
+    {
+        synchronized ( loggerRefs )
+        {
+            loggerRefs.clear();
+        }
+    }
+
+    /**
+     * Add a reference to a newly available LogService
+     */
+    void addLoggerRef( final ServiceReference ref )
+    {
+        synchronized (loggerRefs)
+        {
+            loggerRefs.add(ref);
+        }
+    }
+
+    /**
+     * Remove a reference to a LogService
+     */
+    void removeLoggerRef( final ServiceReference ref )
+    {
+        synchronized (loggerRefs)
+        {
+            loggerRefs.remove(ref);
+        }
+    }
+
+    /**
+     * Set the context of the bundle in the singleton implementation.
+     */
+    private void setBundleContext(final BundleContext context)
+    {
+        synchronized(loggerRefs)
+        {
+            this.context = context;
+        }
+    }
+
+    public void log(final int level, final String msg)
+    {
+        log(null, level, msg, null);
+    }
+
+    public void log(final int level, final String msg, final Throwable ex)
+    {
+        log(null, level, msg, null);
+    }
+
+    public void log(final ServiceReference sr, final int level, final String msg)
+    {
+        log(sr, level, msg, null);
+    }
+
+    public void log(final ServiceReference sr, final int level, final String msg,
+        final Throwable ex)
+    {
+        // The method will remove any unregistered service reference as well.
+        synchronized (loggerRefs)
+        {
+            if (level > logLevel)
+            {
+                return; // don't log
+            }
+
+            boolean logged = false;
+
+            if (!loggerRefs.isEmpty())
+            {
+                // There is at least one LogService available hence, we can use the
+                // class as well.
+                for (Iterator<ServiceReference> iter = loggerRefs.iterator(); iter.hasNext();)
+                {
+                    final ServiceReference next = iter.next();
+
+                    org.osgi.service.log.LogService logger =
+                        (org.osgi.service.log.LogService) context.getService(next);
+
+                    if (null != logger)
+                    {
+                        if ( sr == null )
+                        {
+                            if ( ex == null )
+                            {
+                                logger.log(level, msg);
+                            }
+                            else
+                            {
+                                logger.log(level, msg, ex);
+                            }
+                        }
+                        else
+                        {
+                            if ( ex == null )
+                            {
+                                logger.log(sr, level, msg);
+                            }
+                            else
+                            {
+                                logger.log(sr, level, msg, ex);
+                            }
+                        }
+                        context.ungetService(next);
+                        // we logged, so we can finish
+                        logged = true;
+                        break;
+                    }
+                    else
+                    {
+                        // The context returned null for the reference - it follows
+                        // that the service is unregistered and we can remove it
+                        iter.remove();
+                    }
+                }
+            }
+            if ( !logged)
+            {
+                _log(sr, level, msg, ex);
+            }
+        }
+    }
+
+    /*
+     * Log the message to standard output. This appends the level to the message.
+     * null values are handled appropriate.
+     */
+    private void _log(final ServiceReference sr, final int level, final String msg,
+        Throwable ex)
+    {
+        String s = (sr == null) ? null : "SvcRef " + sr;
+        s = (s == null) ? msg : s + " " + msg;
+        s = (ex == null) ? s : s + " (" + ex + ")";
+
+        switch (level)
+        {
+            case LOG_DEBUG:
+                System.out.println("DEBUG: " + s);
+                break;
+            case LOG_ERROR:
+                System.out.println("ERROR: " + s);
+                if (ex != null)
+                {
+                    if ((ex instanceof BundleException)
+                        && (((BundleException) ex).getNestedException() != null))
+                    {
+                        ex = ((BundleException) ex).getNestedException();
+                    }
+
+                    ex.printStackTrace();
+                }
+                break;
+            case LOG_INFO:
+                System.out.println("INFO: " + s);
+                break;
+            case LOG_WARNING:
+                System.out.println("WARNING: " + s);
+                break;
+            default:
+                System.out.println("UNKNOWN[" + level + "]: " + s);
+        }
+    }
+
+    /**
+     * Change the current log level. Log level decides what messages gets
+     * logged. Any message with a log level higher than the currently set
+     * log level is not logged.
+     *
+     * @param logLevel new log level
+     */
+    public void setLogLevel(int logLevel)
+    {
+        synchronized (loggerRefs)
+        {
+            logLevel = logLevel;
+        }
+    }
+
+    /**
+     * @return current log level.
+     */
+    public int getLogLevel()
+    {
+        synchronized (loggerRefs)
+        {
+            return logLevel;
+        }
+    }
+}

Propchange: felix/trunk/coordinator/src/main/java/org/apache/felix/coordinator/impl/LogWrapper.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/coordinator/src/main/java/org/apache/felix/coordinator/impl/LogWrapper.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url

Propchange: felix/trunk/coordinator/src/main/java/org/apache/felix/coordinator/impl/LogWrapper.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain