FELIX-2647 : Implement Coordinator Service - start implementing nested coordinations and fix failing CT test cases
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1550646 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/coordinator/src/main/java/org/apache/felix/coordinator/impl/CoordinationImpl.java b/coordinator/src/main/java/org/apache/felix/coordinator/impl/CoordinationImpl.java
index 92aedfa..1da5428 100644
--- a/coordinator/src/main/java/org/apache/felix/coordinator/impl/CoordinationImpl.java
+++ b/coordinator/src/main/java/org/apache/felix/coordinator/impl/CoordinationImpl.java
@@ -99,11 +99,13 @@
{
if (startTermination())
{
+ this.owner.unregister(this);
this.failReason = reason;
// consider failure reason (if not null)
- for (Participant part : participants)
+ for (int i=participants.size()-1;i>=0;i--)
{
+ final Participant part = participants.get(i);
try
{
part.failed(this);
@@ -131,11 +133,16 @@
public void end()
{
+
if (startTermination())
{
- boolean partialFailure = false;
- for (Participant part : participants)
+ this.owner.endNestedCoordinations(this);
+ this.owner.unregister(this);
+
+ boolean partialFailure = false;
+ for (int i=participants.size()-1;i>=0;i--)
{
+ final Participant part = participants.get(i);
try
{
part.ended(this);
@@ -287,8 +294,10 @@
public Coordination push()
{
- // TODO: Check whether this has already been pushed !
- // throw new CoordinationException("Coordination already pushed", this, CoordinationException.ALREADY_PUSHED);
+ if ( isTerminated() )
+ {
+ throw new CoordinationException("Coordination already ended", this, CoordinationException.ALREADY_ENDED);
+ }
return owner.push(this);
}
@@ -329,7 +338,6 @@
if (state == ACTIVE)
{
state = TERMINATING;
- owner.unregister(this);
scheduleTimeout(-1);
return true;
}
@@ -368,13 +376,37 @@
}
}
- public Bundle getBundle() {
- // TODO Auto-generated method stub
- return null;
+ public Bundle getBundle()
+ {
+ return this.owner.getBundle();
}
- public Coordination getEnclosingCoordination() {
- // TODO Auto-generated method stub
- return null;
+ public Coordination getEnclosingCoordination()
+ {
+ return this.owner.getEnclosingCoordination(this);
}
+
+ @Override
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + (int) (id ^ (id >>> 32));
+ return result;
+ }
+
+ @Override
+ public boolean equals(final Object obj)
+ {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ final CoordinationImpl other = (CoordinationImpl) obj;
+ if (id != other.id)
+ return false;
+ return true;
+ }
}
diff --git a/coordinator/src/main/java/org/apache/felix/coordinator/impl/CoordinationMgr.java b/coordinator/src/main/java/org/apache/felix/coordinator/impl/CoordinationMgr.java
index 93dd4af..5cd66ec 100644
--- a/coordinator/src/main/java/org/apache/felix/coordinator/impl/CoordinationMgr.java
+++ b/coordinator/src/main/java/org/apache/felix/coordinator/impl/CoordinationMgr.java
@@ -206,6 +206,13 @@
stack = new Stack<Coordination>();
threadStacks.set(stack);
}
+ else
+ {
+ if ( stack.contains(c) )
+ {
+ throw new CoordinationException("Coordination already pushed", c, CoordinationException.ALREADY_PUSHED);
+ }
+ }
return stack.push(c);
}
@@ -318,4 +325,36 @@
{ c.getId(), c.getName(), c.getDeadLine() });
}
*/
+
+ public Coordination getEnclosingCoordination(final CoordinationImpl c)
+ {
+ Stack<Coordination> stack = threadStacks.get();
+ if ( stack != null )
+ {
+ final int index = stack.indexOf(c);
+ if ( index > 0 )
+ {
+ return stack.elementAt(index - 1);
+ }
+ }
+ return null;
+ }
+
+ public void endNestedCoordinations(final CoordinationImpl c)
+ {
+ Stack<Coordination> stack = threadStacks.get();
+ if ( stack != null )
+ {
+ final int index = stack.indexOf(c) + 1;
+ if ( index > 0 && stack.size() > index )
+ {
+ final int count = stack.size()-index;
+ for(int i=0;i<count;i++)
+ {
+ final Coordination nested = (Coordination)stack.pop();
+ nested.end();
+ }
+ }
+ }
+ }
}
diff --git a/coordinator/src/main/java/org/apache/felix/coordinator/impl/CoordinatorImpl.java b/coordinator/src/main/java/org/apache/felix/coordinator/impl/CoordinatorImpl.java
index fa1cfea..504b764 100644
--- a/coordinator/src/main/java/org/apache/felix/coordinator/impl/CoordinatorImpl.java
+++ b/coordinator/src/main/java/org/apache/felix/coordinator/impl/CoordinatorImpl.java
@@ -20,6 +20,7 @@
import java.util.Collection;
import java.util.HashSet;
+import java.util.Set;
import java.util.TimerTask;
import org.osgi.framework.Bundle;
@@ -27,7 +28,6 @@
import org.osgi.service.coordinator.CoordinationException;
import org.osgi.service.coordinator.Participant;
-@SuppressWarnings("deprecation")
public class CoordinatorImpl implements org.osgi.service.coordinator.Coordinator
{
@@ -35,7 +35,7 @@
private final CoordinationMgr mgr;
- private final HashSet<Coordination> coordinations;
+ private final Set<Coordination> coordinations;
CoordinatorImpl(final Bundle owner, final CoordinationMgr mgr)
{
@@ -72,17 +72,89 @@
if (active != null)
{
Throwable reason = new Exception("Coordinator service released");
- for (int i = 0; i < active.length; i++)
+ for (int i = active.length -1; i >=0; i--)
{
active[i].fail(reason);
}
}
}
+ /**
+ * Ensures the <code>name</code> complies with the <em>symbolic-name</em>
+ * production of the OSGi core specification (1.3.2):
+ *
+ * <pre>
+ * symbolic-name :: = token('.'token)*
+ * digit ::= [0..9]
+ * alpha ::= [a..zA..Z]
+ * alphanum ::= alpha | digit
+ * token ::= ( alphanum | ’_’ | ’-’ )+
+ * </pre>
+ *
+ * If the key does not comply an <code>IllegalArgumentException</code> is
+ * thrown.
+ *
+ * @param key
+ * The configuration property key to check.
+ * @throws IllegalArgumentException
+ * if the key does not comply with the symbolic-name production.
+ */
+ private void checkName( final String name )
+ {
+ // check for empty string
+ if ( name.length() == 0 )
+ {
+ throw new IllegalArgumentException( "Name must not be an empty string" );
+ }
+ final String[] parts = name.split("\\.");
+ for(final String p : parts)
+ {
+ boolean valid = true;
+ if ( p.length() == 0 )
+ {
+ valid = false;
+ }
+ else
+ {
+ for(int i=0; i<p.length(); i++)
+ {
+ final char c = p.charAt(i);
+ if ( c >= '0' && c <= '9') {
+ continue;
+ }
+ if ( c >= 'a' && c <= 'z') {
+ continue;
+ }
+ if ( c >= 'A' && c <= 'Z') {
+ continue;
+ }
+ if ( c == '_' || c == '-') {
+ continue;
+ }
+ valid = false;
+ break;
+ }
+ }
+ if ( !valid )
+ {
+ throw new IllegalArgumentException( "Name [" + name + "] does not comply with the symbolic-name definition." );
+ }
+ }
+ }
+
public Coordination create(final String name, final long timeout)
{
// TODO: check permission
- Coordination c = mgr.create(this, name, timeout);
+
+ // check arguments
+ checkName(name);
+ if ( timeout < 0 )
+ {
+ throw new IllegalArgumentException("Timeout must not be negative");
+ }
+
+ // create coordination
+ final Coordination c = mgr.create(this, name, timeout);
synchronized (coordinations)
{
coordinations.add(c);
@@ -174,4 +246,19 @@
{
mgr.releaseParticipant(p);
}
+
+ Bundle getBundle()
+ {
+ return this.owner;
+ }
+
+ public Coordination getEnclosingCoordination(final CoordinationImpl c)
+ {
+ return mgr.getEnclosingCoordination(c);
+ }
+
+ public void endNestedCoordinations(final CoordinationImpl c)
+ {
+ this.mgr.endNestedCoordinations(c);
+ }
}
diff --git a/coordinator/src/test/java/org/apache/felix/coordinator/impl/CoordinatorImplTest.java b/coordinator/src/test/java/org/apache/felix/coordinator/impl/CoordinatorImplTest.java
index 1aea579..21433e1 100644
--- a/coordinator/src/test/java/org/apache/felix/coordinator/impl/CoordinatorImplTest.java
+++ b/coordinator/src/test/java/org/apache/felix/coordinator/impl/CoordinatorImplTest.java
@@ -156,9 +156,17 @@
assertEquals(c2, coordinator.peek());
c1.end();
- assertEquals(c2, coordinator.peek());
+ assertNull(coordinator.peek());
- c2.end();
+ try
+ {
+ c2.end();
+ fail("c2 is already terminated");
+ }
+ catch (CoordinationException ce)
+ {
+ assertEquals(CoordinationException.ALREADY_ENDED, ce.getType());
+ }
assertNull(coordinator.peek());
}
@@ -192,7 +200,7 @@
assertEquals(c2, p21.c);
assertTrue(p22.ended);
assertEquals(c2, p22.c);
- assertTrue("p21 must be called before p22", p21.time < p22.time);
+ assertTrue("p22 must be called before p21", p22.time < p21.time);
// assert order of call with two registrations
final Coordination c3 = coordinator.create(name, 0);
@@ -210,7 +218,7 @@
assertEquals(c3, p31.c);
assertTrue(p32.ended);
assertEquals(c3, p32.c);
- assertTrue("p21 must be called before p22", p31.time < p32.time);
+ assertTrue("p32 must be called before p31", p32.time < p31.time);
}
public void test_addParticipant_with_failed()
@@ -243,7 +251,7 @@
assertEquals(c2, p21.c);
assertTrue(p22.failed);
assertEquals(c2, p22.c);
- assertTrue("p21 must be called before p22", p21.time < p22.time);
+ assertTrue("p22 must be called before p21", p22.time < p21.time);
// assert order of call with two registrations
final Coordination c3 = coordinator.create(name, 0);
@@ -261,7 +269,7 @@
assertEquals(c3, p31.c);
assertTrue(p32.failed);
assertEquals(c3, p32.c);
- assertTrue("p21 must be called before p22", p31.time < p32.time);
+ assertTrue("p31 must be called before p32", p32.time < p31.time);
}
public void test_Coordination_timeout() throws InterruptedException