| /* |
| * 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; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertNotSame; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| |
| import java.util.List; |
| import java.util.Map; |
| import java.util.UUID; |
| |
| import org.apache.log4j.Level; |
| import org.apache.log4j.LogManager; |
| import org.apache.sling.discovery.ClusterView; |
| import org.apache.sling.discovery.InstanceDescription; |
| import org.apache.sling.discovery.TopologyEvent; |
| import org.apache.sling.discovery.TopologyEvent.Type; |
| import org.apache.sling.discovery.base.commons.UndefinedClusterViewException; |
| import org.apache.sling.discovery.base.its.setup.VirtualInstance; |
| import org.apache.sling.discovery.base.its.setup.VirtualInstanceBuilder; |
| import org.apache.sling.discovery.base.its.setup.mock.AssertingTopologyEventListener; |
| import org.apache.sling.discovery.base.its.setup.mock.PropertyProviderImpl; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| public abstract class AbstractSingleInstanceTest { |
| |
| private final Logger logger = LoggerFactory.getLogger(this.getClass()); |
| |
| VirtualInstance instance; |
| |
| String propertyValue; |
| |
| private Level logLevel; |
| |
| protected abstract VirtualInstanceBuilder newBuilder(); |
| |
| @Before |
| public void setup() throws Exception { |
| final org.apache.log4j.Logger discoveryLogger = LogManager.getRootLogger().getLogger("org.apache.sling.discovery"); |
| logLevel = discoveryLogger.getLevel(); |
| discoveryLogger.setLevel(Level.DEBUG); |
| logger.info("setup: creating new standalone instance"); |
| instance = newBuilder().setDebugName("standaloneInstance") |
| .newRepository("/var/discovery/impl/", true) |
| .setConnectorPingTimeout(20) |
| .setConnectorPingInterval(999)/*long enough heartbeat interval to prevent them to disturb the explicit heartbeats during the test*/ |
| .setMinEventDelay(3).build(); |
| logger.info("setup: creating new standalone instance done."); |
| } |
| |
| @After |
| public void tearDown() throws Exception { |
| final org.apache.log4j.Logger discoveryLogger = LogManager.getRootLogger().getLogger("org.apache.sling.discovery"); |
| discoveryLogger.setLevel(logLevel); |
| logger.info("tearDown: stopping standalone instance"); |
| if (instance!=null) { |
| instance.stop(); |
| instance = null; |
| } |
| logger.info("tearDown: stopping standalone instance done"); |
| } |
| |
| @Test |
| public void testGetters() throws UndefinedClusterViewException, InterruptedException { |
| logger.info("testGetters: start"); |
| assertNotNull(instance); |
| logger.info("sling id=" + instance.getSlingId()); |
| try{ |
| instance.getClusterViewService().getLocalClusterView(); |
| fail("should complain"); // SLING-5030 |
| } catch(UndefinedClusterViewException e) { |
| // ok |
| } |
| |
| instance.heartbeatsAndCheckView(); |
| // wait 4000ms for the vote to happen |
| Thread.sleep(4000); |
| |
| assertNotNull(instance.getClusterViewService().getLocalClusterView()); |
| ClusterView cv = instance.getClusterViewService().getLocalClusterView(); |
| logger.info("cluster view: id=" + cv.getId()); |
| assertNotNull(cv.getId()); |
| assertNotSame(cv.getId(), ""); |
| |
| List<InstanceDescription> instances = cv.getInstances(); |
| assertNotNull(instances); |
| assertTrue(instances.size() == 1); |
| |
| InstanceDescription myInstance = instances.get(0); |
| assertNotNull(myInstance); |
| assertTrue(myInstance.getClusterView() == cv); |
| logger.info("instance id: " + myInstance.getSlingId()); |
| assertEquals(instance.getSlingId(), myInstance.getSlingId()); |
| |
| Map<String, String> properties = myInstance.getProperties(); |
| assertNotNull(properties); |
| |
| assertNull(myInstance.getProperty("foo")); |
| |
| assertTrue(myInstance.isLeader()); |
| |
| assertTrue(myInstance.isLocal()); |
| logger.info("testGetters: end"); |
| } |
| |
| @Test |
| public void testPropertyProviders() throws Throwable { |
| logger.info("testPropertyProviders: start"); |
| final String propertyName = UUID.randomUUID().toString(); |
| propertyValue = UUID.randomUUID().toString(); |
| PropertyProviderImpl pp = new PropertyProviderImpl(); |
| pp.setProperty(propertyName, propertyValue); |
| instance.bindPropertyProvider(pp, propertyName); |
| |
| instance.heartbeatsAndCheckView(); |
| // wait 4000ms for the vote to happen |
| Thread.sleep(4000); |
| assertEquals(propertyValue, |
| instance.getClusterViewService().getLocalClusterView() |
| .getInstances().get(0).getProperty(propertyName)); |
| |
| propertyValue = UUID.randomUUID().toString(); |
| pp.setProperty(propertyName, propertyValue); |
| instance.heartbeatsAndCheckView(); |
| |
| assertEquals(propertyValue, |
| instance.getClusterViewService().getLocalClusterView() |
| .getInstances().get(0).getProperty(propertyName)); |
| assertNull(instance.getClusterViewService().getLocalClusterView() |
| .getInstances().get(0) |
| .getProperty(UUID.randomUUID().toString())); |
| logger.info("testPropertyProviders: end"); |
| } |
| |
| @Test |
| public void testInvalidProperties() throws Throwable { |
| logger.info("testInvalidProperties: start"); |
| |
| instance.heartbeatsAndCheckView(); |
| instance.heartbeatsAndCheckView(); |
| |
| final String propertyValue = UUID.randomUUID().toString(); |
| Thread.sleep(2000); |
| doTestProperty(UUID.randomUUID().toString(), propertyValue, propertyValue); |
| |
| doTestProperty("", propertyValue, null); |
| doTestProperty("-", propertyValue, propertyValue); |
| doTestProperty("_", propertyValue, propertyValue); |
| doTestProperty("jcr:" + UUID.randomUUID().toString(), propertyValue, null); |
| doTestProperty("var/" + UUID.randomUUID().toString(), propertyValue, null); |
| doTestProperty(UUID.randomUUID().toString() + "@test", propertyValue, null); |
| doTestProperty(UUID.randomUUID().toString() + "!test", propertyValue, null); |
| logger.info("testInvalidProperties: end"); |
| } |
| |
| private void doTestProperty(final String propertyName, |
| final String propertyValue, |
| final String expectedPropertyValue) throws Throwable { |
| PropertyProviderImpl pp = new PropertyProviderImpl(); |
| pp.setProperty(propertyName, propertyValue); |
| instance.bindPropertyProvider(pp, propertyName); |
| assertEquals(expectedPropertyValue, |
| instance.getClusterViewService().getLocalClusterView() |
| .getInstances().get(0).getProperty(propertyName)); |
| } |
| |
| @Test |
| public void testTopologyEventListeners() throws Throwable { |
| logger.info("testTopologyEventListeners: start"); |
| instance.heartbeatsAndCheckView(); |
| logger.info("testTopologyEventListeners: 1st sleep 2s"); |
| Thread.sleep(2000); |
| instance.heartbeatsAndCheckView(); |
| logger.info("testTopologyEventListeners: 2nd sleep 2s"); |
| Thread.sleep(2000); |
| |
| AssertingTopologyEventListener assertingTopologyEventListener = new AssertingTopologyEventListener(); |
| assertingTopologyEventListener.addExpected(Type.TOPOLOGY_INIT); |
| logger.info("testTopologyEventListeners: binding the event listener"); |
| instance.bindTopologyEventListener(assertingTopologyEventListener); |
| Thread.sleep(1000); // SLING-4755: async event sending requires some minimal wait time nowadays |
| assertEquals(0, assertingTopologyEventListener.getRemainingExpectedCount()); |
| |
| final String propertyName = UUID.randomUUID().toString(); |
| propertyValue = UUID.randomUUID().toString(); |
| PropertyProviderImpl pp = new PropertyProviderImpl(); |
| pp.setProperty(propertyName, propertyValue); |
| |
| assertingTopologyEventListener.addExpected(Type.PROPERTIES_CHANGED); |
| |
| assertEquals(1, assertingTopologyEventListener.getRemainingExpectedCount()); |
| assertEquals(0, pp.getGetCnt()); |
| instance.bindPropertyProvider(pp, propertyName); |
| logger.info("testTopologyEventListeners: 3rd sleep 1.5s"); |
| Thread.sleep(1500); |
| logger.info("testTopologyEventListeners: dumping due to failure: "); |
| assertingTopologyEventListener.dump(); |
| assertEquals(0, assertingTopologyEventListener.getRemainingExpectedCount()); |
| // we can only assume that the getProperty was called at least once - it |
| // could be called multiple times though.. |
| assertTrue(pp.getGetCnt() > 0); |
| |
| assertingTopologyEventListener.addExpected(Type.PROPERTIES_CHANGED); |
| |
| assertEquals(1, assertingTopologyEventListener.getRemainingExpectedCount()); |
| pp.setGetCnt(0); |
| propertyValue = UUID.randomUUID().toString(); |
| pp.setProperty(propertyName, propertyValue); |
| assertEquals(0, pp.getGetCnt()); |
| instance.heartbeatsAndCheckView(); |
| logger.info("testTopologyEventListeners: 4th sleep 2s"); |
| Thread.sleep(2000); |
| assertEquals(0, assertingTopologyEventListener.getRemainingExpectedCount()); |
| assertEquals(2, pp.getGetCnt()); |
| |
| // a heartbeat repeat should not result in another call though |
| instance.heartbeatsAndCheckView(); |
| logger.info("testTopologyEventListeners: 5th sleep 2s"); |
| Thread.sleep(2000); |
| assertEquals(0, assertingTopologyEventListener.getRemainingExpectedCount()); |
| assertEquals(3, pp.getGetCnt()); |
| logger.info("testTopologyEventListeners: done"); |
| } |
| |
| @Test |
| public void testBootstrap() throws Throwable { |
| logger.info("testBootstrap: start"); |
| try{ |
| instance.getClusterViewService().getLocalClusterView(); |
| fail("should complain"); |
| } catch(UndefinedClusterViewException e) { |
| // SLING-5030 : isolated mode is gone, replaced with exception |
| // ok |
| } |
| |
| // SLING-3750 : with delaying the init event, we now should NOT get any events |
| // before we let the view establish (which happens via heartbeats below) |
| AssertingTopologyEventListener ada = new AssertingTopologyEventListener(); |
| instance.bindTopologyEventListener(ada); |
| assertEquals(0, ada.getEvents().size()); |
| assertEquals(0, ada.getUnexpectedCount()); |
| |
| try{ |
| instance.getClusterViewService().getLocalClusterView(); |
| fail("should complain"); |
| } catch(UndefinedClusterViewException e) { |
| // ok |
| } |
| |
| ada.addExpected(Type.TOPOLOGY_INIT); |
| instance.heartbeatsAndCheckView(); |
| Thread.sleep(1000); |
| instance.heartbeatsAndCheckView(); |
| Thread.sleep(1000); |
| logger.info("testBoostrap: dumping repo..."); |
| instance.dumpRepo(); |
| logger.info("testBoostrap: dumping listener..."); |
| ada.dump(); |
| assertEquals(0, ada.getUnexpectedCount()); |
| assertEquals(1, ada.getEvents().size()); |
| TopologyEvent initEvent = ada.getEvents().remove(0); |
| assertNotNull(initEvent); |
| assertNotNull(initEvent.getNewView()); |
| assertNotNull(initEvent.getNewView().getClusterViews()); |
| |
| // after the view was established though, we expect it to be a normal |
| // EstablishedInstanceDescription |
| instance.assertEstablishedView(); |
| logger.info("testBootstrap: end"); |
| } |
| |
| } |