You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by st...@apache.org on 2015/10/20 16:12:34 UTC

svn commit: r1709601 [7/11] - in /sling/trunk/bundles/extensions/discovery: base/ base/src/ base/src/main/ base/src/main/java/ base/src/main/java/org/ base/src/main/java/org/apache/ base/src/main/java/org/apache/sling/ base/src/main/java/org/apache/sli...

Added: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/FailingScheduler.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/FailingScheduler.java?rev=1709601&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/FailingScheduler.java (added)
+++ sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/FailingScheduler.java Tue Oct 20 14:12:31 2015
@@ -0,0 +1,72 @@
+/*
+ * 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.sling.discovery.base.its.setup.mock;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import org.apache.sling.commons.scheduler.Scheduler;
+
+public class FailingScheduler implements Scheduler {
+    
+    @Override
+    public void removeJob(String name) throws NoSuchElementException {
+        // nothing to do here
+    }
+    
+    @Override
+    public boolean fireJobAt(String name, Object job, Map<String, Serializable> config, Date date, int times, long period) {
+        return false;
+    }
+    
+    @Override
+    public void fireJobAt(String name, Object job, Map<String, Serializable> config, Date date) throws Exception {
+        throw new Exception("cos you are really worth it");
+    }
+    
+    @Override
+    public boolean fireJob(Object job, Map<String, Serializable> config, int times, long period) {
+        return false;
+    }
+    
+    @Override
+    public void fireJob(Object job, Map<String, Serializable> config) throws Exception {
+        throw new Exception("cos you are really worth it");
+    }
+    
+    @Override
+    public void addPeriodicJob(String name, Object job, Map<String, Serializable> config, long period, boolean canRunConcurrently,
+            boolean startImmediate) throws Exception {
+        throw new Exception("cos you are really worth it");
+    }
+    
+    @Override
+    public void addPeriodicJob(String name, Object job, Map<String, Serializable> config, long period, boolean canRunConcurrently)
+            throws Exception {
+        throw new Exception("cos you are really worth it");
+    }
+    
+    @Override
+    public void addJob(String name, Object job, Map<String, Serializable> config, String schedulingExpression,
+            boolean canRunConcurrently) throws Exception {
+        throw new Exception("cos you are really worth it");
+    }
+}
\ No newline at end of file

Propchange: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/FailingScheduler.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/MockFactory.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/MockFactory.java?rev=1709601&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/MockFactory.java (added)
+++ sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/MockFactory.java Tue Oct 20 14:12:31 2015
@@ -0,0 +1,126 @@
+/*
+ * 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.sling.discovery.base.its.setup.mock;
+
+import java.util.Dictionary;
+import java.util.Properties;
+
+import org.apache.sling.api.resource.ResourceResolverFactory;
+import org.apache.sling.jcr.api.SlingRepository;
+import org.apache.sling.settings.SlingSettingsService;
+import org.hamcrest.Description;
+import org.jmock.Expectations;
+import org.jmock.Mockery;
+import org.jmock.api.Action;
+import org.jmock.api.Invocation;
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.jmock.lib.action.ReturnValueAction;
+import org.jmock.lib.action.VoidAction;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.component.ComponentContext;
+
+public class MockFactory {
+
+    public final Mockery context = new JUnit4Mockery();
+
+    public static ResourceResolverFactory mockResourceResolverFactory()
+            throws Exception {
+    	return mockResourceResolverFactory(null);
+    }
+
+    public static ResourceResolverFactory mockResourceResolverFactory(final SlingRepository repositoryOrNull)
+            throws Exception {
+        Mockery context = new JUnit4Mockery();
+
+        final ResourceResolverFactory resourceResolverFactory = context
+                .mock(ResourceResolverFactory.class);
+        // final ResourceResolver resourceResolver = new MockResourceResolver();
+        // final ResourceResolver resourceResolver = new
+        // MockedResourceResolver();
+
+        context.checking(new Expectations() {
+            {
+                allowing(resourceResolverFactory)
+                        .getAdministrativeResourceResolver(null);
+                will(new Action() {
+
+                    public Object invoke(Invocation invocation)
+                            throws Throwable {
+                    	return new MockedResourceResolver(repositoryOrNull);
+                    }
+
+                    public void describeTo(Description arg0) {
+                        arg0.appendText("whateva - im going to create a new mockedresourceresolver");
+                    }
+                });
+            }
+        });
+        return resourceResolverFactory;
+    }
+
+    public static SlingSettingsService mockSlingSettingsService(
+            final String slingId) {
+        Mockery context = new JUnit4Mockery();
+
+        final SlingSettingsService settingsService = context
+                .mock(SlingSettingsService.class);
+        context.checking(new Expectations() {
+            {
+                allowing(settingsService).getSlingId();
+                will(returnValue(slingId));
+                
+                allowing(settingsService).getSlingHomePath();
+                will(returnValue("/n/a"));
+            }
+        });
+        return settingsService;
+    }
+
+    public static ComponentContext mockComponentContext() {
+        Mockery context = new JUnit4Mockery();
+        final BundleContext bc = context.mock(BundleContext.class);
+        context.checking(new Expectations() {
+            {
+                allowing(bc).registerService(with(any(String.class)),
+                        with(any(Object.class)), with(any(Dictionary.class)));
+                will(VoidAction.INSTANCE);
+                
+                allowing(bc).getProperty(with(any(String.class)));
+                will(new ReturnValueAction("foo"));
+            }
+        });
+
+        final ComponentContext cc = context.mock(ComponentContext.class);
+        context.checking(new Expectations() {
+            {
+                allowing(cc).getProperties();
+                will(returnValue(new Properties()));
+
+                allowing(cc).getBundleContext();
+                will(returnValue(bc));
+            }
+        });
+
+        return cc;
+    }
+
+    public static BundleContext mockBundleContext() {
+        return mockComponentContext().getBundleContext();
+    }
+}

Propchange: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/MockFactory.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/MockedResource.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/MockedResource.java?rev=1709601&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/MockedResource.java (added)
+++ sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/MockedResource.java Tue Oct 20 14:12:31 2015
@@ -0,0 +1,297 @@
+/*
+ * 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.sling.discovery.base.its.setup.mock;
+
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.sling.api.resource.ModifiableValueMap;
+import org.apache.sling.api.resource.SyntheticResource;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.api.wrappers.ValueMapDecorator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MockedResource extends SyntheticResource {
+
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    private final MockedResourceResolver mockedResourceResolver;
+    private Session session;
+
+    public MockedResource(MockedResourceResolver resourceResolver, String path,
+            String resourceType) {
+        super(resourceResolver, path, resourceType);
+        mockedResourceResolver = resourceResolver;
+
+        resourceResolver.register(this);
+    }
+
+    private Session getSession() {
+        synchronized (this) {
+            if (session == null) {
+                try {
+                    session = mockedResourceResolver.getSession();
+                } catch (RepositoryException e) {
+                    throw new RuntimeException("RepositoryException: " + e, e);
+                }
+            }
+            return session;
+        }
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+//        close();
+        super.finalize();
+    }
+
+    public void close() {
+        synchronized (this) {
+            if (session != null) {
+                if (session.isLive()) {
+                    session.logout();
+                }
+                session = null;
+            }
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
+        if (type.equals(Node.class)) {
+            try {
+                return (AdapterType) getSession().getNode(getPath());
+            } catch (Exception e) {
+                logger.error("Exception occurred: "+e, e);
+                throw new RuntimeException("Exception occurred: " + e, e);
+            }
+        } else if (type.equals(ValueMap.class)) {
+            try {
+                Session session = getSession();
+                Node node = session.getNode(getPath());
+                HashMap<String, Object> map = new HashMap<String, Object>();
+
+                PropertyIterator properties = node.getProperties();
+                while (properties.hasNext()) {
+                    Property p = properties.nextProperty();
+                    if (p.getType() == PropertyType.BOOLEAN) {
+                        map.put(p.getName(), p.getBoolean());
+                    } else if (p.getType() == PropertyType.STRING) {
+                        map.put(p.getName(), p.getString());
+                    } else if (p.getType() == PropertyType.DATE) {
+                        map.put(p.getName(), p.getDate().getTime());
+                    } else if (p.getType() == PropertyType.NAME) {
+                        map.put(p.getName(), p.getName());
+                    } else if (p.getType() == PropertyType.LONG) {
+                        map.put(p.getName(), p.getLong());
+                    } else {
+                        throw new RuntimeException(
+                                "Unsupported property type: " + p.getType());
+                    }
+                }
+                ValueMap valueMap = new ValueMapDecorator(map);
+                return (AdapterType) valueMap;
+            } catch (Exception e) {
+                e.printStackTrace();
+                return null;
+            }
+        } else if (type.equals(ModifiableValueMap.class)) {
+            return (AdapterType) new ModifiableValueMap() {
+                
+                public Collection<Object> values() {
+                    throw new UnsupportedOperationException();
+                }
+                
+                public int size() {
+                    throw new UnsupportedOperationException();
+                }
+                
+                public Object remove(Object arg0) {
+                    Session session = getSession();
+                    try{
+                        final Node node = session.getNode(getPath());
+                        final Property p = node.getProperty(String.valueOf(arg0));
+                        if (p!=null) {
+                        	p.remove();
+                        }
+                        // this is not according to the spec - but OK for tests since
+                        // the return value is never used
+                        return null;
+                    } catch(PathNotFoundException pnfe) {
+                    	// perfectly fine
+                    	return null;
+                    } catch(RepositoryException e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+                
+                public void putAll(Map<? extends String, ? extends Object> arg0) {
+                    throw new UnsupportedOperationException();
+                }
+                
+                public Object put(String arg0, Object arg1) {
+                    Session session = getSession();
+                    try{
+                        final Node node = session.getNode(getPath());
+                        Object result = null;
+                        if (node.hasProperty(arg0)) {
+                            final Property previous = node.getProperty(arg0);
+                            if (previous==null) {
+                                // null
+                            } else if (previous.getType() == PropertyType.STRING) {
+                                result = previous.getString();
+                            } else if (previous.getType() == PropertyType.DATE) {
+                                result = previous.getDate();
+                            } else if (previous.getType() == PropertyType.BOOLEAN) {
+                                result = previous.getBoolean();
+                            } else if (previous.getType() == PropertyType.LONG) {
+                                result = previous.getLong();
+                            } else {
+                                throw new UnsupportedOperationException();
+                            }
+                        }
+                        if (arg1 instanceof String) {
+                            node.setProperty(arg0, (String)arg1);
+                        } else if (arg1 instanceof Calendar) {
+                            node.setProperty(arg0, (Calendar)arg1);
+                        } else if (arg1 instanceof Boolean) {
+                            node.setProperty(arg0, (Boolean)arg1);
+                        } else if (arg1 instanceof Date) {
+                            final Calendar c = Calendar.getInstance();
+                            c.setTime((Date)arg1);
+                            node.setProperty(arg0, c);
+                        } else if (arg1 instanceof Long) {
+                            node.setProperty(arg0, (Long)arg1);
+                        } else {
+                            throw new UnsupportedOperationException();
+                        }
+                        return result;
+                    } catch(RepositoryException e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+                
+                public Set<String> keySet() {
+                    Session session = getSession();
+                    try {
+                        final Node node = session.getNode(getPath());
+                        final PropertyIterator pi = node.getProperties();
+                        final Set<String> result = new HashSet<String>();
+                        while(pi.hasNext()) {
+                            final Property p = pi.nextProperty();
+                            result.add(p.getName());
+                        }
+                        return result;
+                    } catch (RepositoryException e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+                
+                public boolean isEmpty() {
+                    throw new UnsupportedOperationException();
+                }
+                
+                public Object get(Object arg0) {
+                    Session session = getSession();
+                    try{
+                        final Node node = session.getNode(getPath());
+                        final String key = String.valueOf(arg0);
+                        if (node.hasProperty(key)) {
+                            return node.getProperty(key);
+                        } else {
+                            return null;
+                        }
+                    } catch(RepositoryException re) {
+                        throw new RuntimeException(re);
+                    }
+                }
+                
+                public Set<Entry<String, Object>> entrySet() {
+                    throw new UnsupportedOperationException();
+                }
+                
+                public boolean containsValue(Object arg0) {
+                    throw new UnsupportedOperationException();
+                }
+                
+                public boolean containsKey(Object arg0) {
+                    Session session = getSession();
+                    try{
+                        final Node node = session.getNode(getPath());
+                        return node.hasProperty(String.valueOf(arg0));
+                    } catch(RepositoryException re) {
+                        throw new RuntimeException(re);
+                    }
+                }
+                
+                public void clear() {
+                    throw new UnsupportedOperationException();
+                }
+                
+                public <T> T get(String name, T defaultValue) {
+                    throw new UnsupportedOperationException();
+                }
+                
+                public <T> T get(String name, Class<T> type) {
+                    Session session = getSession();
+                    try{
+                        final Node node = session.getNode(getPath());
+                        if (node==null) {
+                        	return null;
+                        }
+                        if (!node.hasProperty(name)) {
+                            return null;
+                        }
+                        Property p = node.getProperty(name);
+                        if (p==null) {
+                        	return null;
+                        }
+                        if (type.equals(Calendar.class)) {
+                        	return (T) p.getDate();
+                        } else if (type.equals(String.class)) {
+                        	return (T) p.getString();
+                        } else {
+                            throw new UnsupportedOperationException();
+                        }
+                    } catch(RepositoryException e) {
+                    	throw new RuntimeException(e);
+                    }
+                }
+            };
+        } else {
+            return super.adaptTo(type);
+        }
+    }
+
+}

Propchange: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/MockedResource.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/MockedResourceResolver.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/MockedResourceResolver.java?rev=1709601&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/MockedResourceResolver.java (added)
+++ sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/MockedResourceResolver.java Tue Oct 20 14:12:31 2015
@@ -0,0 +1,335 @@
+/*
+ * 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.sling.discovery.base.its.setup.mock;
+
+import java.io.IOException;
+import java.util.Calendar;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.jcr.Credentials;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.PersistenceException;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.commons.testing.jcr.RepositoryProvider;
+import org.apache.sling.commons.testing.jcr.RepositoryUtil;
+import org.apache.sling.jcr.api.SlingRepository;
+import org.slf4j.LoggerFactory;
+
+public class MockedResourceResolver implements ResourceResolver {
+
+	private final SlingRepository repository;
+
+	private Session session;
+
+    private List<MockedResource> resources = new LinkedList<MockedResource>();
+
+    public MockedResourceResolver() throws RepositoryException {
+    	this(null);
+    }
+
+    public MockedResourceResolver(SlingRepository repositoryOrNull) throws RepositoryException {
+    	if (repositoryOrNull==null) {
+    		this.repository = RepositoryProvider.instance().getRepository();
+    		Session adminSession = null;
+    		try {
+    		    adminSession = this.repository.loginAdministrative(null);
+                RepositoryUtil.registerSlingNodeTypes(adminSession);
+    		} catch ( final IOException ioe ) {
+    		    throw new RepositoryException(ioe);
+    		} finally {
+    		    if ( adminSession != null ) {
+    		        adminSession.logout();
+    		    }
+    		}
+    	} else {
+    		this.repository = repositoryOrNull;
+    	}
+    }
+
+    public Session getSession() throws RepositoryException {
+        synchronized (this) {
+            if (session != null) {
+                return session;
+            }
+            session = createSession();
+            return session;
+        }
+    }
+
+    private Repository getRepository() {
+    	return repository;
+    }
+
+    private Session createSession() throws RepositoryException {
+        final Credentials credentials = new SimpleCredentials("admin",
+                "admin".toCharArray());
+        return repository.login(credentials, "default");
+    }
+
+
+    @SuppressWarnings("unchecked")
+    public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
+        if (type.equals(Session.class)) {
+            try {
+                return (AdapterType) getSession();
+            } catch (RepositoryException e) {
+                throw new RuntimeException("RepositoryException: " + e, e);
+            }
+        } else if (type.equals(Repository.class)) {
+        	return (AdapterType) getRepository();
+        }
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    public Resource resolve(HttpServletRequest request, String absPath) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    public Resource resolve(String absPath) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Deprecated
+    public Resource resolve(HttpServletRequest request) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    public String map(String resourcePath) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    public String map(HttpServletRequest request, String resourcePath) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    public Resource getResource(String path) {
+        Session session;
+        try {
+            session = getSession();
+            session.getNode(path);
+        } catch (PathNotFoundException e) {
+            return null;
+        } catch (RepositoryException e) {
+            throw new RuntimeException("RepositoryException: " + e, e);
+        }
+        return new MockedResource(this, path, "nt:unstructured");
+    }
+
+    public Resource getResource(Resource base, String path) {
+        if (base.getPath().equals("/")) {
+            return getResource("/" + path);
+        } else {
+            return getResource(base.getPath() + "/" + path);
+        }
+    }
+
+    public String[] getSearchPath() {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    public Iterator<Resource> listChildren(Resource parent) {
+        try {
+            Node node = parent.adaptTo(Node.class);
+            final NodeIterator nodes = node.getNodes();
+            return new Iterator<Resource>() {
+
+                public void remove() {
+                    throw new UnsupportedOperationException();
+                }
+
+                public Resource next() {
+                    Node next = nodes.nextNode();
+                    try {
+                        return new MockedResource(MockedResourceResolver.this,
+                                next.getPath(), "nt:unstructured");
+                    } catch (RepositoryException e) {
+                        throw new RuntimeException("RepositoryException: " + e,
+                                e);
+                    }
+                }
+
+                public boolean hasNext() {
+                    return nodes.hasNext();
+                }
+            };
+        } catch (RepositoryException e) {
+            throw new RuntimeException("RepositoryException: " + e, e);
+        }
+    }
+
+    public Iterable<Resource> getChildren(Resource parent) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    public Iterator<Resource> findResources(String query, String language) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    public Iterator<Map<String, Object>> queryResources(String query,
+            String language) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    public ResourceResolver clone(Map<String, Object> authenticationInfo)
+            throws LoginException {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    public boolean isLive() {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    public void close() {
+        Iterator<MockedResource> it = resources.iterator();
+        while (it.hasNext()) {
+            MockedResource r = it.next();
+            r.close();
+        }
+        if (session != null) {
+            if (session.isLive()) {
+                session.logout();
+            }
+            session = null;
+        }
+    }
+
+    public void register(MockedResource mockedResource) {
+        resources.add(mockedResource);
+    }
+
+    public String getUserID() {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    public Iterator<String> getAttributeNames() {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    public Object getAttribute(String name) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    public void delete(Resource resource) throws PersistenceException {
+        if (resources.contains(resource)) {
+            resources.remove(resource);
+            Node node = resource.adaptTo(Node.class);
+            try {
+                node.remove();
+            } catch (RepositoryException e) {
+                throw new PersistenceException("RepositoryException: "+e, e);
+            }
+        } else {
+            throw new UnsupportedOperationException("Not implemented");
+        }
+    }
+
+    public Resource create(Resource parent, String name,
+            Map<String, Object> properties) throws PersistenceException {
+        final Node parentNode = parent.adaptTo(Node.class);
+        boolean success = false;
+        try {
+            final Node child;
+            if (properties!=null && properties.containsKey("jcr:primaryType")) {
+                child = parentNode.addNode(name, (String) properties.get("jcr:primaryType"));
+            } else {
+                child = parentNode.addNode(name);
+            }
+            if (properties!=null) {
+                final Iterator<Entry<String, Object>> it = properties.entrySet().iterator();
+                while(it.hasNext()) {
+                    final Entry<String, Object> entry = it.next();
+                    if (entry.getKey().equals("jcr:primaryType")) {
+                        continue;
+                    }
+                    if (entry.getValue() instanceof String) {
+                        child.setProperty(entry.getKey(), (String)entry.getValue());
+                    } else if (entry.getValue() instanceof Boolean) {
+                        child.setProperty(entry.getKey(), (Boolean)entry.getValue());
+                    } else if (entry.getValue() instanceof Calendar) {
+                        child.setProperty(entry.getKey(), (Calendar)entry.getValue());
+                    } else {
+                        throw new UnsupportedOperationException("Not implemented (entry.getValue(): "+entry.getValue()+")");
+                    }
+                }
+            }
+            Resource result = getResource(parent, name);
+            success = true;
+            return result;
+        } catch (RepositoryException e) {
+            throw new RuntimeException(e);
+        } finally {
+            LoggerFactory.getLogger(this.getClass()).info("create: creating of "+name+" under "+parent+" was successful="+success);
+        }
+    }
+
+    public void revert() {
+        try {
+            this.session.refresh(false);
+        } catch (final RepositoryException re) {
+            throw new RuntimeException("Unable to commit changes.", re);
+        }
+    }
+
+    public void commit() throws PersistenceException {
+        try {
+            this.session.save();
+        } catch (final RepositoryException re) {
+            throw new PersistenceException("Unable to commit changes.", re);
+        }
+    }
+
+    public boolean hasChanges() {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    public String getParentResourceType(Resource resource) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public String getParentResourceType(String resourceType) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public boolean isResourceType(Resource resource, String resourceType) {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    public void refresh() {
+        // TODO Auto-generated method stub
+
+    }
+
+}

Propchange: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/MockedResourceResolver.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/PropertyProviderImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/PropertyProviderImpl.java?rev=1709601&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/PropertyProviderImpl.java (added)
+++ sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/PropertyProviderImpl.java Tue Oct 20 14:12:31 2015
@@ -0,0 +1,58 @@
+/*
+ * 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.sling.discovery.base.its.setup.mock;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.sling.discovery.PropertyProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class PropertyProviderImpl implements PropertyProvider {
+
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    private final Map<String, String> properties = new HashMap<String, String>();
+
+    private int getCnt = 0;
+
+    public PropertyProviderImpl() {
+        // nothing so far
+    }
+
+    public String getProperty(String name) {
+        getCnt++;
+        logger.warn("getProperty: name="+name+", new getCnt="+getCnt, new Exception("getProperty-stacktrace"));
+        return properties.get(name);
+    }
+
+    public void setProperty(String name, String value) {
+        properties.put(name, value);
+    }
+
+    public void setGetCnt(int getCnt) {
+        this.getCnt = getCnt;
+    }
+
+    public int getGetCnt() {
+        return getCnt;
+    }
+
+}

Propchange: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/PropertyProviderImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/SimpleClusterViewService.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/SimpleClusterViewService.java?rev=1709601&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/SimpleClusterViewService.java (added)
+++ sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/SimpleClusterViewService.java Tue Oct 20 14:12:31 2015
@@ -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.sling.discovery.base.its.setup.mock;
+
+import java.util.HashMap;
+import java.util.UUID;
+
+import org.apache.sling.discovery.base.commons.ClusterViewService;
+import org.apache.sling.discovery.base.commons.UndefinedClusterViewException;
+import org.apache.sling.discovery.commons.providers.DefaultInstanceDescription;
+import org.apache.sling.discovery.commons.providers.spi.LocalClusterView;
+
+public class SimpleClusterViewService implements ClusterViewService {
+
+    private LocalClusterView clusterView;
+    
+    private final String slingId;
+
+    public SimpleClusterViewService(String slingId) {
+        this.slingId = slingId;
+        LocalClusterView clusterView = new LocalClusterView(UUID.randomUUID().toString(), null);
+        new DefaultInstanceDescription(clusterView, true, true, slingId, new HashMap<String, String>());
+        this.clusterView = clusterView;
+    }
+    
+    @Override
+    public String getSlingId() {
+        return slingId;
+    }
+
+    @Override
+    public LocalClusterView getLocalClusterView() throws UndefinedClusterViewException {
+        if (clusterView==null) {
+            throw new IllegalStateException("no clusterView set");
+        }
+        return clusterView;
+    }
+    
+    public void setClusterView(LocalClusterView clusterView) {
+        this.clusterView = clusterView;
+    }
+
+}

Propchange: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/SimpleClusterViewService.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/SimpleConnectorConfig.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/SimpleConnectorConfig.java?rev=1709601&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/SimpleConnectorConfig.java (added)
+++ sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/SimpleConnectorConfig.java Tue Oct 20 14:12:31 2015
@@ -0,0 +1,215 @@
+/*
+ * 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.sling.discovery.base.its.setup.mock;
+
+import java.net.URL;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.sling.discovery.base.its.setup.ModifiableTestBaseConfig;
+
+public class SimpleConnectorConfig implements ModifiableTestBaseConfig {
+
+    private int connectionTimeout;
+    private int soTimeout;
+    private URL[] topologyConnectorURLs;
+    private List<String> topologyConnectorWhitelist;
+    private String clusterInstancesPath = "/var/discovery/impl/clusterInstances";
+    private boolean hmacEnabled;
+    private String sharedKey;
+    private long keyInterval;
+    private boolean encryptionEnabled;
+    private boolean gzipConnectorRequestsEnabled;
+    private boolean autoStopLocalLoopEnabled;
+    private int backoffStandbyFactor;
+    private int backoffStableFactor;
+    private long backoffStandbyInterval;
+    private long announcementInterval = 20;
+    private long announcementTimeout = 20;
+    private int minEventDelay;
+
+    @Override
+    public int getSocketConnectionTimeout() {
+        return connectionTimeout;
+    }
+    
+    public void setConnectionTimeout(int connectionTimeout) {
+        this.connectionTimeout = connectionTimeout;
+    }
+
+    @Override
+    public int getSoTimeout() {
+        return soTimeout;
+    }
+    
+    public void setSoTimeout(int soTimeout) {
+        this.soTimeout = soTimeout;
+    }
+
+    @Override
+    public URL[] getTopologyConnectorURLs() {
+        return topologyConnectorURLs;
+    }
+    
+    public void setTopologyConnectorURLs(URL[] topologyConnectorURLs) {
+        this.topologyConnectorURLs = topologyConnectorURLs;
+    }
+
+    @Override
+    public String[] getTopologyConnectorWhitelist() {
+        if (topologyConnectorWhitelist==null) {
+            return null;
+        }
+        return topologyConnectorWhitelist.toArray(new String[topologyConnectorWhitelist.size()]);
+    }
+
+    public void addTopologyConnectorWhitelistEntry(String whitelistEntry) {
+        if (topologyConnectorWhitelist==null) {
+            topologyConnectorWhitelist = new LinkedList<String>();
+        }
+        topologyConnectorWhitelist.add(whitelistEntry);
+    }
+
+    @Override
+    public String getClusterInstancesPath() {
+        return clusterInstancesPath;
+    }
+    
+    public void setClusterInstancesPath(String clusterInstancesPath) {
+        this.clusterInstancesPath = clusterInstancesPath;
+    }
+
+    @Override
+    public boolean isHmacEnabled() {
+        return hmacEnabled;
+    }
+    
+    public void setHmacEnabled(boolean hmacEnabled) {
+        this.hmacEnabled = hmacEnabled;
+    }
+
+    @Override
+    public String getSharedKey() {
+        return sharedKey;
+    }
+    
+    public void setSharedKey(String sharedKey) {
+        this.sharedKey = sharedKey;
+    }
+
+    @Override
+    public long getKeyInterval() {
+        return keyInterval;
+    }
+    
+    public void setKeyInterval(int keyInterval) {
+        this.keyInterval = keyInterval;
+    }
+
+    @Override
+    public boolean isEncryptionEnabled() {
+        return encryptionEnabled;
+    }
+    
+    public void setEncryptionEnabled(boolean encryptionEnabled) {
+        this.encryptionEnabled = encryptionEnabled;
+    }
+
+    @Override
+    public boolean isGzipConnectorRequestsEnabled() {
+        return gzipConnectorRequestsEnabled;
+    }
+    
+    public void setGzipConnectorRequestsEnabled(boolean gzipConnectorRequestsEnabled) {
+        this.gzipConnectorRequestsEnabled = gzipConnectorRequestsEnabled;
+    }
+
+    @Override
+    public boolean isAutoStopLocalLoopEnabled() {
+        return autoStopLocalLoopEnabled;
+    }
+    
+    public void setAutoStopLocalLoopEnabled(boolean autoStopLocalLoopEnabled) {
+        this.autoStopLocalLoopEnabled = autoStopLocalLoopEnabled;
+    }
+
+    @Override
+    public int getBackoffStandbyFactor() {
+        return backoffStandbyFactor;
+    }
+    
+    public void setBackoffStandbyFactor(int backoffStandbyFactor) {
+        this.backoffStandbyFactor = backoffStandbyFactor;
+    }
+
+    @Override
+    public int getBackoffStableFactor() {
+        return backoffStableFactor;
+    }
+    
+    public void setBackoffStableFactor(int backoffStableFactor) {
+        this.backoffStableFactor = backoffStableFactor;
+    }
+
+    @Override
+    public long getBackoffStandbyInterval() {
+        return backoffStandbyInterval;
+    }
+    
+    public void setBackoffStandbyInterval(long backoffStandbyInterval) {
+        this.backoffStandbyInterval = backoffStandbyInterval;
+    }
+
+    @Override
+    public long getConnectorPingInterval() {
+        return announcementInterval;
+    }
+    
+    public void setAnnouncementInterval(long announcementInterval) {
+        this.announcementInterval = announcementInterval;
+    }
+
+    @Override
+    public long getConnectorPingTimeout() {
+        return announcementTimeout;
+    }
+    
+    public void setAnnouncementTimeout(long announcementTimeout) {
+        this.announcementTimeout = announcementTimeout;
+    }
+
+    @Override
+    public int getMinEventDelay() {
+        return minEventDelay;
+    }
+    
+    public void setMinEventDelay(int minEventDelay) {
+        this.minEventDelay = minEventDelay;
+    }
+
+    @Override
+    public void setViewCheckTimeout(int viewCheckTimeout) {
+        announcementTimeout = viewCheckTimeout;
+    }
+
+    @Override
+    public void setViewCheckInterval(int viewCheckInterval) {
+        announcementInterval = viewCheckInterval;
+    }
+}

Propchange: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/SimpleConnectorConfig.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/TopologyEventAsserter.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/TopologyEventAsserter.java?rev=1709601&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/TopologyEventAsserter.java (added)
+++ sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/TopologyEventAsserter.java Tue Oct 20 14:12:31 2015
@@ -0,0 +1,25 @@
+/*
+ * 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.sling.discovery.base.its.setup.mock;
+
+import org.apache.sling.discovery.TopologyEvent;
+
+public interface TopologyEventAsserter {
+    public void assertOk(TopologyEvent event);
+}
\ No newline at end of file

Propchange: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/TopologyEventAsserter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: sling/trunk/bundles/extensions/discovery/base/src/test/resources/log4j.properties
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/base/src/test/resources/log4j.properties?rev=1709601&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/discovery/base/src/test/resources/log4j.properties (added)
+++ sling/trunk/bundles/extensions/discovery/base/src/test/resources/log4j.properties Tue Oct 20 14:12:31 2015
@@ -0,0 +1,26 @@
+# 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.
+
+log4j.rootLogger=INFO, stdout
+
+log4j.logger.org.apache.jackrabbit.core.TransientRepository=WARN
+#log4j.logger.org.apache.sling.discovery.impl=DEBUG
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+#log4j.appender.stdout.layout.ConversionPattern=%d{dd.MM.yyyy HH:mm:ss} *%-5p* [%t] %c{1}: %m (%F, line %L)\n
+log4j.appender.stdout.layout.ConversionPattern=%d{dd.MM.yyyy HH:mm:ss} *%-5p* [%t] %c{1}: %m\n

Propchange: sling/trunk/bundles/extensions/discovery/base/src/test/resources/log4j.properties
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: sling/trunk/bundles/extensions/discovery/commons/pom.xml
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/commons/pom.xml?rev=1709601&r1=1709600&r2=1709601&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/discovery/commons/pom.xml (original)
+++ sling/trunk/bundles/extensions/discovery/commons/pom.xml Tue Oct 20 14:12:31 2015
@@ -31,10 +31,8 @@
     <packaging>bundle</packaging>
     <version>1.0.0-SNAPSHOT</version>
 
-    <name>Apache Sling Discovery Commons Bundle</name>
-    <description>
-        Commons services related to Sling Discovery.
-    </description>
+    <name>Apache Sling Discovery Commons</name>
+    <description>Common services related to Sling Discovery</description>
 
     <scm>
         <connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/bundles/extensions/discovery/commons</connection>
@@ -49,6 +47,21 @@
                 <artifactId>maven-bundle-plugin</artifactId>
                 <extensions>true</extensions>
             </plugin>
+			<!-- discovery.commons exports a few test classes for reuse.
+                 In order for others to use these, the test-jar must be built/installed too.
+                 Note that 'mvn -Dmaven.test.skip=true' does NOT build the test-jar,
+                 however 'mvn -DskipTests' does. -->
+             <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>test-jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
         </plugins>
     </build>
     <dependencies>
@@ -175,5 +188,11 @@
         	<type>bundle</type>
         	<scope>test</scope>
         </dependency>
+		<dependency>
+			<groupId>org.apache.sling</groupId>
+			<artifactId>org.apache.sling.settings</artifactId>
+			<version>1.2.2</version>
+            <scope>provided</scope>
+		</dependency>
     </dependencies>
 </project>

Modified: sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/package-info.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/package-info.java?rev=1709601&r1=1709600&r2=1709601&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/package-info.java (original)
+++ sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/package-info.java Tue Oct 20 14:12:31 2015
@@ -18,7 +18,7 @@
  */
 
 /**
- * Provides commons utility for the Discovery API.
+ * Provides commons utility for users for the Discovery API.
  *
  * @version 1.0.0
  */

Added: sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/DefaultClusterView.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/DefaultClusterView.java?rev=1709601&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/DefaultClusterView.java (added)
+++ sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/DefaultClusterView.java Tue Oct 20 14:12:31 2015
@@ -0,0 +1,180 @@
+/*
+ * 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.sling.discovery.commons.providers;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.sling.discovery.ClusterView;
+import org.apache.sling.discovery.InstanceDescription;
+
+/**
+ * Default implementation of the ClusterView interface.
+ * <p>
+ * Besides implementing the interface methods it also
+ * adds add/remove of InstanceDescriptions as well as 
+ * implementing equals and hashCode.
+ */
+public class DefaultClusterView implements ClusterView {
+
+    /** the id of this cluster view **/
+    private final String id;
+
+    /** the list of instances as part of this cluster **/
+    private final List<InstanceDescription> instances = new LinkedList<InstanceDescription>();
+
+    public DefaultClusterView(final String id) {
+        if (id == null || id.length() == 0) {
+            throw new IllegalArgumentException("id must not be null");
+        }
+        this.id = id;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj == null || !(obj instanceof DefaultClusterView)) {
+            return false;
+        }
+        final DefaultClusterView other = (DefaultClusterView) obj;
+        if (!this.id.equals(other.id)) {
+            return false;
+        }
+        if (!this.getLeader().equals(other.getLeader())) {
+            return false;
+        }
+        if (this.instances.size() != other.instances.size()) {
+            return false;
+        }
+        for (Iterator<InstanceDescription> it = instances.iterator(); it
+                .hasNext();) {
+            InstanceDescription instance = it.next();
+            if (!other.instances.contains(instance)) {
+                return false;
+            }
+        }
+        return true;
+    }
+    
+    @Override
+    public String toString() {
+        if (instances.size() == 0) {
+            return "a ClusterView[no instances]";
+        } else if (instances.size() == 1) {
+            return "a ClusterView[1 instance: "+instances.get(0).getSlingId()+"]";
+        } else {
+            StringBuffer sb = new StringBuffer();
+            for (InstanceDescription id : instances) {
+                if (sb.length() != 0) {
+                    sb.append(", ");
+                }
+                sb.append(id.getSlingId());
+            }
+            return "a ClusterView[" + instances.size() + " instances: " + sb.toString() + "]";
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return id.hashCode();
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    /**
+     * Add the given instance to this cluster and set the cluster on the instance (back pointer)
+     * @param instance the instance to add to this cluster
+     */
+    public void addInstanceDescription(final DefaultInstanceDescription instance) {
+        if (instances.contains(instance)) {
+            throw new IllegalArgumentException("cannot add same instance twice");
+        }
+        if (instance.isLeader() && doGetLeader() != null) {
+            throw new IllegalArgumentException(
+                    "cannot add another leader. there already is one");
+        }
+        instances.add(instance);
+        instance.setClusterView(this);
+    }
+
+    public List<InstanceDescription> getInstances() {
+        if (instances.size() == 0) {
+            throw new IllegalStateException("no instance was ever added");
+        }
+        return Collections.unmodifiableList(instances);
+    }
+
+    public InstanceDescription getLeader() {
+        final InstanceDescription result = doGetLeader();
+        if (result != null) {
+            return result;
+        }
+        throw new IllegalStateException("no leader was added");
+    }
+
+    /**
+     * Lookup the leader of this cluster
+     * @return the leader of this cluster - should never return null
+     */
+    private InstanceDescription doGetLeader() {
+        for (Iterator<InstanceDescription> it = instances.iterator(); it
+                .hasNext();) {
+            InstanceDescription anInstance = it.next();
+            if (anInstance.isLeader()) {
+                return anInstance;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Removes the given instance from this cluster.
+     * <p>
+     * Note that the instance will still have a pointer to this cluster however.
+     * @param instance the instance to remove from this cluster
+     */
+    public boolean removeInstanceDescription(InstanceDescription instance) {
+        return instances.remove(instance);
+    }
+    
+    /**
+     * Returns the local InstanceDescription or null if no local instance is listed
+     * @return the local InstanceDescription or null if no local instance is listed
+     * @throws IllegalStateException if multiple local instances are listed
+     */
+    public InstanceDescription getLocalInstance() {
+        InstanceDescription local = null;
+        for (Iterator<InstanceDescription> it = getInstances().iterator(); 
+                it.hasNext();) {
+            InstanceDescription instance = it.next();
+            if (instance.isLocal()) {
+                if (local!=null) {
+                    throw new IllegalStateException("found multiple local instances!?");
+                }
+                local = instance;
+                break;
+            }
+        }
+        return local;
+    }
+
+}

Propchange: sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/DefaultClusterView.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/DefaultInstanceDescription.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/DefaultInstanceDescription.java?rev=1709601&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/DefaultInstanceDescription.java (added)
+++ sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/DefaultInstanceDescription.java Tue Oct 20 14:12:31 2015
@@ -0,0 +1,202 @@
+/*
+ * 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.sling.discovery.commons.providers;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.apache.sling.discovery.ClusterView;
+import org.apache.sling.discovery.InstanceDescription;
+import org.apache.sling.discovery.commons.providers.util.PropertyNameHelper;
+
+/**
+ * Base implementation for the InstanceDescription interface.
+ * <p>
+ * Allows creation of the object with clusterview and/or properties null - to be
+ * set later but before usage!
+ * <p>
+ */
+public class DefaultInstanceDescription implements InstanceDescription {
+
+    /** the cluster view of which this instance is part of **/
+    private ClusterView clusterView;
+
+    /** whether this instance is the leader in the cluster **/
+    private boolean isLeader;
+
+    /** whether this instance is the local/own one **/
+    private boolean isLocal;
+
+    /** the sling id of this instance **/
+    private String slingId;
+
+    /** the properties of this instance **/
+    private Map<String, String> properties;
+
+    public DefaultInstanceDescription(final DefaultClusterView clusterView,
+            final boolean isLeader, final boolean isOwn, final String slingId,
+            final Map<String, String> properties) {
+        // slingId must not be null - clusterView and properties can be null though
+        if (slingId == null || slingId.length() == 0) {
+            throw new IllegalArgumentException("slingId must not be null");
+        }
+        this.isLeader = isLeader;
+        this.isLocal = isOwn;
+        this.slingId = slingId;
+        this.properties = filterValidProperties(properties);
+        if (clusterView != null) {
+            clusterView.addInstanceDescription(this);
+            if (this.clusterView == null) {
+                throw new IllegalStateException(
+                        "clusterView should have been set by now");
+            }
+        }
+    }
+
+    @Override
+    public String toString() {
+    	final String clusterInfo;
+    	if (clusterView==null) {
+    		clusterInfo = "";
+    	} else {
+    		clusterInfo = ", clusterViewId="+clusterView.getId();
+    	}
+        return "an InstanceDescription[slindId=" + slingId + ", isLeader="
+                + isLeader + ", isOwn=" + isLocal + clusterInfo + ", properties=" + this.properties + "]";
+    }
+
+    @Override
+    public int hashCode() {
+        return slingId.hashCode();
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj == null || !(obj instanceof DefaultInstanceDescription)) {
+            return false;
+        }
+        final DefaultInstanceDescription other = (DefaultInstanceDescription) obj;
+        if (!this.slingId.equals(other.slingId)) {
+            return false;
+        }
+        if (!this.slingId.equals(other.slingId)) {
+            return false;
+        }
+        if (!properties.equals(other.properties)) {
+            return false;
+        }
+        if (!this.getClusterView().getId()
+                .equals(other.getClusterView().getId())) {
+            return false;
+        }
+        return true;
+    }
+
+    public ClusterView getClusterView() {
+        if (clusterView == null) {
+            throw new IllegalStateException("clusterView was never set");
+        }
+        return clusterView;
+    }
+
+    /**
+     * Sets the cluster on this instance
+     * @param clusterView
+     */
+    void setClusterView(ClusterView clusterView) {
+        if (this.clusterView != null) {
+            throw new IllegalStateException("can only set clusterView once");
+        }
+        if (clusterView == null) {
+            throw new IllegalArgumentException("clusterView must not be null");
+        }
+        this.clusterView = clusterView;
+    }
+
+    public boolean isLeader() {
+        return isLeader;
+    }
+
+    public boolean isLocal() {
+        return isLocal;
+    }
+
+    public String getSlingId() {
+        return slingId;
+    }
+
+    public String getProperty(final String name) {
+        if (properties == null) {
+            throw new IllegalStateException("properties were never set");
+        }
+        return properties.get(name);
+    }
+
+    public Map<String, String> getProperties() {
+        if (properties == null) {
+            throw new IllegalStateException("properties were never set");
+        }
+        return Collections.unmodifiableMap(properties);
+    }
+    
+    /**
+     * Sets the properties of this instance
+     * @param properties
+     */
+    protected void setProperties(final Map<String, String> properties) {
+        if (properties == null) {
+            throw new IllegalArgumentException("properties must not be null");
+        }
+        this.properties = filterValidProperties(properties);
+    }
+
+    /** SLING-2883 : filter (pass-through) valid properties only **/
+	private Map<String, String> filterValidProperties(
+			Map<String, String> rawProps) {
+		if (rawProps==null) {
+			return null;
+		}
+
+		final HashMap<String, String> filteredProps = new HashMap<String, String>();
+		final Set<Entry<String, String>> entries = rawProps.entrySet();
+		final Iterator<Entry<String, String>> it = entries.iterator();
+		while(it.hasNext()) {
+			final Entry<String, String> anEntry = it.next();
+			if (PropertyNameHelper.isValidPropertyName(anEntry.getKey())) {
+				filteredProps.put(anEntry.getKey(), anEntry.getValue());
+			}
+		}
+		return filteredProps;
+	}
+
+	/** for testing only! **/
+    public void setProperty(String key, String value) {
+        if (!PropertyNameHelper.isValidPropertyName(key)) {
+            throw new IllegalArgumentException("key is not a valid property name: "+key);
+        }
+        if (properties == null) {
+            properties = new HashMap<String, String>();
+        }
+        properties.put(key, value);
+    }
+}

Propchange: sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/DefaultInstanceDescription.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/NonLocalInstanceDescription.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/NonLocalInstanceDescription.java?rev=1709601&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/NonLocalInstanceDescription.java (added)
+++ sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/NonLocalInstanceDescription.java Tue Oct 20 14:12:31 2015
@@ -0,0 +1,38 @@
+/*
+ * 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.sling.discovery.commons.providers;
+
+import java.util.Map;
+
+import org.apache.sling.discovery.commons.providers.DefaultClusterView;
+import org.apache.sling.discovery.commons.providers.DefaultInstanceDescription;
+
+/**
+ * InstanceDescription which represents an instance that is explicitly
+ * not local, ie isOwn==false.
+ */
+public class NonLocalInstanceDescription extends DefaultInstanceDescription {
+
+    public NonLocalInstanceDescription(final DefaultClusterView cluster,
+            final boolean isLeader, final String slingId, final Map<String, String> properties) {
+        // isOwn==false
+        super(cluster, isLeader, false, slingId, properties);
+    }
+
+}

Propchange: sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/NonLocalInstanceDescription.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/ViewStateManager.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/ViewStateManager.java?rev=1709601&r1=1709600&r2=1709601&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/ViewStateManager.java (original)
+++ sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/ViewStateManager.java Tue Oct 20 14:12:31 2015
@@ -88,4 +88,14 @@ public interface ViewStateManager {
      */
     void handleNewView(BaseTopologyView newView);
 
+    /** 
+     * for testing only: wait for any potentially queued async events to be delivered 
+     * before returning.
+     * <p>
+     * @param timeout time in millis to wait for at max - 0 to not wait at all - -1 
+     * to wait indefinitely
+     * @return true if no more async events exist, false if the timeout hit early 
+     */
+    boolean waitForAsyncEvents(long timeout);
+
 }
\ No newline at end of file

Modified: sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/impl/MinEventDelayHandler.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/impl/MinEventDelayHandler.java?rev=1709601&r1=1709600&r2=1709601&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/impl/MinEventDelayHandler.java (original)
+++ sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/impl/MinEventDelayHandler.java Tue Oct 20 14:12:31 2015
@@ -58,6 +58,9 @@ class MinEventDelayHandler {
             throw new IllegalArgumentException("discoveryService must not be null");
         }
         this.discoveryService = discoveryService;
+        if (scheduler==null) {
+            throw new IllegalArgumentException("scheduler must not be null");
+        }
         this.scheduler = scheduler;
         if (minEventDelaySecs<=0) {
             throw new IllegalArgumentException("minEventDelaySecs must be greater than 0 (is "+minEventDelaySecs+")");
@@ -79,10 +82,18 @@ class MinEventDelayHandler {
             return true;
         }
         
-        if (!viewStateManager.hadPreviousView() 
-                || viewStateManager.isPropertiesDiff(newView) 
-                || viewStateManager.unchanged(newView)) {
-            logger.info("handleNewView: we never had a previous view, so we mustn't delay");
+        if (!viewStateManager.hadPreviousView()) {
+            logger.info("handlesNewView: never had a previous view, hence no delaying applicable");
+            return false;
+        }
+        
+        if (viewStateManager.isPropertiesDiff(newView)) {
+            logger.info("handlesNewView: only properties differ, hence no delaying applicable");
+            return false;
+        }
+        
+        if (viewStateManager.unchanged(newView)) {
+            logger.info("handlesNewView: view is unchanged, hence no delaying applicable");
             return false;
         }
         
@@ -90,6 +101,15 @@ class MinEventDelayHandler {
         if (!triggerAsyncDelaying(newView)) {
             logger.info("handleNewView: could not trigger async delaying, sending new view now.");
             viewStateManager.handleNewViewNonDelayed(newView);
+        } else {
+            // if triggering the async event was successful, then we should also
+            // ensure that we sent out a TOPOLOGY_CHANGING *before* that delayed event hits.
+            //
+            // and, we're still in lock.lock() - so we are safe to do a handleChanging() here
+            // even though there is the very unlikely possibility that the async-delay-thread
+            // would compete - but even if it would, thanks to the lock.lock() that would be safe.
+            // so: we're going to do a handleChanging here:
+            viewStateManager.handleChanging();
         }
         return true;
     }
@@ -165,4 +185,9 @@ class MinEventDelayHandler {
         }
     }
 
+    /** for testing only **/
+    public boolean isDelaying() {
+        return isDelaying;
+    }
+
 }

Modified: sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/impl/ViewStateManagerImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/impl/ViewStateManagerImpl.java?rev=1709601&r1=1709600&r2=1709601&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/impl/ViewStateManagerImpl.java (original)
+++ sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/impl/ViewStateManagerImpl.java Tue Oct 20 14:12:31 2015
@@ -52,7 +52,7 @@ import org.slf4j.LoggerFactory;
  * appropriately. Additionally, the ConsistencyService callback will
  * also be locked using the provided lock object.
  */
-class ViewStateManagerImpl implements ViewStateManager {
+public class ViewStateManagerImpl implements ViewStateManager {
 
     private static final Logger logger = LoggerFactory.getLogger(ViewStateManagerImpl.class);
     
@@ -412,10 +412,13 @@ class ViewStateManagerImpl implements Vi
         if (minEventDelayHandler!=null) {
             if (minEventDelayHandler.handlesNewView(newView)) {
                 return;// true;
+            } else {
+                logger.debug("handleNewView: event delaying not applicable this time, invoking hanldeNewViewNonDelayed next.");
             }
+        } else {
+            logger.debug("handleNewView: minEventDelayHandler not set, invoking hanldeNewViewNonDelayed...");
         }
-        logger.debug("handleNewView: minEventDelayHandler not set or not applicable this time, invoking hanldeNewViewNonDelayed...");
-        /*return */handleNewViewNonDelayed(newView);
+        handleNewViewNonDelayed(newView);
     }
 
     boolean handleNewViewNonDelayed(final BaseTopologyView newView) {
@@ -521,10 +524,11 @@ class ViewStateManagerImpl implements Vi
                         try{
                             logger.debug("consistencyService.callback.run: lock aquired. (modCnt should be {}, is {})", lastModCnt, modCnt);
                             if (modCnt!=lastModCnt) {
-                                logger.debug("consistencyService.callback.run: modCnt changed (from {} to {}) - ignoring",
+                                logger.info("consistencyService.callback.run: modCnt changed (from {} to {}) - ignoring",
                                         lastModCnt, modCnt);
                                 return;
                             }
+                            logger.info("consistencyService.callback.run: invoking doHandleConsistent.");
                             // else:
                             doHandleConsistent(newView);
                         } finally {
@@ -619,5 +623,24 @@ class ViewStateManagerImpl implements Vi
     AsyncEventSender getAsyncEventSender() {
         return asyncEventSender;
     }
+
+    @Override
+    public boolean waitForAsyncEvents(long timeout) {
+        long end = System.currentTimeMillis() + timeout;
+        while(asyncEventSender.hasInFlightEvent() || 
+                (minEventDelayHandler!=null && minEventDelayHandler.isDelaying())) {
+            if (timeout==0) {
+                return false;
+            }
+            if (timeout<0 || System.currentTimeMillis()<end) {
+                try {
+                    Thread.sleep(50);
+                } catch (InterruptedException e) {
+                    // ignore
+                }
+            }
+        }
+        return true;
+    }
     
 }

Added: sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/impl/package-info.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/impl/package-info.java?rev=1709601&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/impl/package-info.java (added)
+++ sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/impl/package-info.java Tue Oct 20 14:12:31 2015
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+/**
+ * Provides commons implementations for providers of the Discovery API.
+ *
+ * @version 1.0.0
+ */
+@Version("1.0.0")
+package org.apache.sling.discovery.commons.providers.impl;
+
+import aQute.bnd.annotation.Version;
+

Propchange: sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/impl/package-info.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/package-info.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/package-info.java?rev=1709601&r1=1709600&r2=1709601&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/package-info.java (original)
+++ sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/package-info.java Tue Oct 20 14:12:31 2015
@@ -18,7 +18,7 @@
  */
 
 /**
- * Provides commons utility for the Discovery API.
+ * Provides commons utility for providers of the Discovery API.
  *
  * @version 1.0.0
  */

Added: sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/spi/LocalClusterView.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/spi/LocalClusterView.java?rev=1709601&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/spi/LocalClusterView.java (added)
+++ sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/spi/LocalClusterView.java Tue Oct 20 14:12:31 2015
@@ -0,0 +1,36 @@
+/*
+ * 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.sling.discovery.commons.providers.spi;
+
+import org.apache.sling.discovery.commons.providers.DefaultClusterView;
+
+public class LocalClusterView extends DefaultClusterView {
+
+    private final String localClusterSyncTokenId;
+
+    public LocalClusterView(String id, String localClusterSyncTokenId) {
+        super(id);
+        this.localClusterSyncTokenId = localClusterSyncTokenId;
+    }
+    
+    public String getLocalClusterSyncTokenId() {
+        return localClusterSyncTokenId;
+    }
+
+}

Propchange: sling/trunk/bundles/extensions/discovery/commons/src/main/java/org/apache/sling/discovery/commons/providers/spi/LocalClusterView.java
------------------------------------------------------------------------------
    svn:eol-style = native