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