/* * 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.brooklyn.core.feed; import java.util.Map; import java.util.concurrent.Callable; import org.apache.brooklyn.api.entity.Entity; import org.apache.brooklyn.api.entity.EntitySpec; import org.apache.brooklyn.api.entity.ImplementedBy; import org.apache.brooklyn.api.mgmt.Task; import org.apache.brooklyn.api.sensor.AttributeSensor; import org.apache.brooklyn.config.ConfigKey; import org.apache.brooklyn.core.config.ConfigKeys; import org.apache.brooklyn.core.entity.AbstractEntity; import org.apache.brooklyn.core.entity.EntityAsserts; import org.apache.brooklyn.core.sensor.Sensors; import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport; import org.apache.brooklyn.feed.function.FunctionFeed; import org.apache.brooklyn.feed.function.FunctionPollConfig; import org.apache.brooklyn.util.core.task.DynamicTasks; import org.apache.brooklyn.util.core.task.Tasks; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import com.google.common.base.Functions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; public class PollerTest extends BrooklynAppUnitTestSupport { @DataProvider(name = "specProvider") public Object[][] specProvider() { EntitySpec<FeedExceptionEntity> pollFailer = EntitySpec.create(FeedExceptionEntity.class) .configure(FeedExceptionEntity.POLLER, new PollFailer()); EntitySpec<FeedExceptionEntity> taskFailer = EntitySpec.create(FeedExceptionEntity.class) .configure(FeedExceptionEntity.POLLER, new TaskFailer()); return new Object[][]{{pollFailer}, {taskFailer}}; } @Test(dataProvider = "specProvider") public void testFeedContinuesWhenPollerThrows(EntitySpec<FeedExceptionEntity> spec) { Map<?, ?> timeoutFlags = ImmutableMap.of("timeout", "100ms"); FeedExceptionEntity fee = app.createAndManageChild(spec); app.start(ImmutableList.of(app.newSimulatedLocation())); EntityAsserts.assertAttributeEqualsEventually(timeoutFlags, fee, FeedExceptionEntity.FLAG, true); fee.startThrowingPollExceptions(); EntityAsserts.assertAttributeEqualsEventually(timeoutFlags, fee, FeedExceptionEntity.FLAG, false); EntityAsserts.assertAttributeEqualsContinually(timeoutFlags, fee, FeedExceptionEntity.FLAG, false); fee.stopThrowingPollExceptions(); EntityAsserts.assertAttributeEqualsEventually(timeoutFlags, fee, FeedExceptionEntity.FLAG, true); EntityAsserts.assertAttributeEqualsContinually(timeoutFlags, fee, FeedExceptionEntity.FLAG, true); } @ImplementedBy(FeedExceptionEntityImpl.class) public static interface FeedExceptionEntity extends Entity { ConfigKey<ThrowingPoller> POLLER = ConfigKeys.newConfigKey(ThrowingPoller.class, "poller"); AttributeSensor<Boolean> FLAG = Sensors.newBooleanSensor("flag"); void startThrowingPollExceptions(); void stopThrowingPollExceptions(); } public static class FeedExceptionEntityImpl extends AbstractEntity implements FeedExceptionEntity { private ThrowingPoller poller; @Override public void init() { super.init(); poller = config().get(POLLER); FunctionFeed.builder() .entity(this) .period(1L) .poll(new FunctionPollConfig<Boolean, Boolean>(FLAG) .callable(poller) .onException(Functions.constant(false))) .build(); } public void startThrowingPollExceptions() { this.poller.setShouldThrow(true); } public void stopThrowingPollExceptions() { this.poller.setShouldThrow(false); } } private static class TaskFailer extends ThrowingPoller { public Boolean execute(final boolean shouldThrow) { Task<Boolean> t = Tasks.<Boolean>builder() .body(new Callable<Boolean>() { @Override public Boolean call() { if (shouldThrow) { throw new IllegalArgumentException("exception in feed task"); } return true; } }) .build(); return DynamicTasks.queueIfPossible(t).orSubmitAsync().asTask().getUnchecked(); } } private static class PollFailer extends ThrowingPoller { public Boolean execute(final boolean shouldThrow) { if (shouldThrow) { throw new IllegalArgumentException("exception in poller"); } return true; } } private static abstract class ThrowingPoller implements Callable<Boolean> { protected final Object throwLock = new Object[0]; boolean shouldThrow = false; abstract Boolean execute(boolean shouldThrow); @Override public Boolean call() throws Exception { synchronized (throwLock) { return execute(shouldThrow); } } public void setShouldThrow(boolean shouldThrow) { synchronized (throwLock) { this.shouldThrow = shouldThrow; } } } }