Java tutorial
/* * 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.cloudfoundry.entity; import com.google.common.base.Functions; import com.google.common.base.Optional; import com.google.common.collect.Iterables; import org.apache.brooklyn.api.entity.Entity; import org.apache.brooklyn.api.entity.drivers.EntityDriverManager; import org.apache.brooklyn.api.location.Location; import org.apache.brooklyn.api.mgmt.Task; import org.apache.brooklyn.cloudfoundry.location.CloudFoundryPaasLocation; import org.apache.brooklyn.core.entity.AbstractEntity; import org.apache.brooklyn.core.entity.BrooklynConfigKeys; import org.apache.brooklyn.core.entity.Entities; import org.apache.brooklyn.core.entity.lifecycle.Lifecycle; import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic; import org.apache.brooklyn.core.location.Locations; import org.apache.brooklyn.feed.function.FunctionFeed; import org.apache.brooklyn.feed.function.FunctionPollConfig; import org.apache.brooklyn.util.collections.MutableList; import org.apache.brooklyn.util.collections.MutableMap; import org.apache.brooklyn.util.core.task.DynamicTasks; import org.apache.brooklyn.util.core.task.Tasks; import org.apache.brooklyn.util.exceptions.Exceptions; import org.apache.brooklyn.util.repeat.Repeater; import org.apache.brooklyn.util.time.Duration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nullable; import java.util.Collection; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; public abstract class CloudFoundryEntityImpl extends AbstractEntity implements CloudFoundryEntity { private static final Logger log = LoggerFactory.getLogger(CloudFoundryEntityImpl.class); private CloudFoundryPaasLocation cfLocation; private FunctionFeed serviceProcessUp; protected boolean connectedSensors = false; private FunctionFeed serviceProcessIsRunning; private EntityPaasDriver driver; public CloudFoundryEntityImpl() { super(MutableMap.of(), null); } public CloudFoundryEntityImpl(Entity parent) { this(MutableMap.of(), parent); } public CloudFoundryEntityImpl(Map properties) { this(properties, null); } public CloudFoundryEntityImpl(Map properties, Entity parent) { super(properties, parent); } public void init() { super.init(); } @Override protected void initEnrichers() { super.initEnrichers(); ServiceStateLogic.ServiceNotUpLogic.updateNotUpIndicator(this, SERVICE_PROCESS_IS_RUNNING, "No information yet on whether this service is running"); } @Override public abstract Class getDriverInterface(); @Override public EntityPaasDriver getDriver() { return driver; } @Override public final void start(final Collection<? extends Location> locations) { if (DynamicTasks.getTaskQueuingContext() != null) { doStart(locations); } else { Task<?> task = Tasks.builder().name("start (sequential)").body(new Runnable() { public void run() { doStart(locations); } }).build(); Entities.submit(this, task).getUnchecked(); } } protected final void doStart(Collection<? extends Location> locations) { ServiceStateLogic.setExpectedState(this, Lifecycle.STARTING); try { preStart(findLocation(locations)); customStart(); log.info("Entity {} was started", new Object[] { this }); postDriverStart(); ServiceStateLogic.setExpectedState(this, Lifecycle.RUNNING); } catch (Throwable t) { ServiceStateLogic.setExpectedState(this, Lifecycle.ON_FIRE); log.error("Error error starting entity {}", this); throw Exceptions.propagate(t); } } protected void preStart(Location location) { this.addLocations(MutableList.of(location)); Optional<CloudFoundryPaasLocation> optional = tryLocation(); if (optional.isPresent()) { cfLocation = optional.get(); } else { throw new ExceptionInInitializerError( "Location should not be null in " + this + " the entity needs a initialized Location"); } initDriver(cfLocation); } /* * TODO: avoiding boilerplate code * This method was gotten getLocations in MachineLifecycleEffectorTasks */ protected Location findLocation(@Nullable Collection<? extends Location> locations) { if (locations == null || locations.isEmpty()) { locations = this.getLocations(); } locations = Locations.getLocationsCheckingAncestors(locations, this); if (locations.isEmpty()) throw new IllegalArgumentException("No locations specified when starting " + this); if (locations.size() != 1 || Iterables.getOnlyElement(locations) == null) throw new IllegalArgumentException( "Ambiguous locations detected when starting " + this + ": " + locations); return Iterables.getOnlyElement(locations); } private Optional<CloudFoundryPaasLocation> tryLocation() { return Optional.fromNullable( Iterables.get(Iterables.filter(getLocations(), CloudFoundryPaasLocation.class), 0, null)); } protected void customStart() { driver.start(); } protected void postDriverStart() { waitForEntityStart(); connectSensors(); Entities.waitForServiceUp(this, Duration.of(getConfig(BrooklynConfigKeys.START_TIMEOUT).toMilliseconds(), TimeUnit.MILLISECONDS)); } protected void connectSensors() { connectedSensors = true; connectServiceIsRunning(); connectServiceUp(); } protected void connectServiceIsRunning() { serviceProcessIsRunning = FunctionFeed.builder().entity(this).period(Duration.FIVE_SECONDS) .poll(new FunctionPollConfig<Boolean, Boolean>(SERVICE_PROCESS_IS_RUNNING) .onException(Functions.constant(Boolean.FALSE)).callable(new Callable<Boolean>() { public Boolean call() { return driver.isRunning(); } })) .build(); } protected void connectServiceUp() { serviceProcessUp = FunctionFeed.builder().entity(this).period(Duration.FIVE_SECONDS) .poll(new FunctionPollConfig<Boolean, Boolean>(SERVICE_UP) .onException(Functions.constant(Boolean.FALSE)).callable(new Callable<Boolean>() { public Boolean call() { return driver.isRunning(); } })) .build(); } @Override public void stop() { if (DynamicTasks.getTaskQueuingContext() != null) { doStop(); } else { Task<?> task = Tasks.builder().name("stop").body(new Runnable() { public void run() { doStop(); } }).build(); Entities.submit(this, task).getUnchecked(); } } /** * To be overridden instead of {@link #stop()}; sub-classes should call {@code super.doStop()} * and should add do additional work via tasks, executed using * {@link org.apache.brooklyn.util.core.task.DynamicTasks#queue(String, java.util.concurrent.Callable)}. */ protected final void doStop() { log.info("Stopping {}", this); if (getAttribute(SERVICE_STATE_ACTUAL).equals(Lifecycle.STOPPED)) { log.warn("The entity {} is already stopped", new Object[] { this }); return; } ServiceStateLogic.setExpectedState(this, Lifecycle.STOPPING); try { preStop(); customStop(); ServiceStateLogic.setExpectedState(this, Lifecycle.STOPPED); log.info("The entity stop operation {} is completed without errors", this); } catch (Throwable t) { ServiceStateLogic.setExpectedState(this, Lifecycle.ON_FIRE); throw Exceptions.propagate(t); } } protected void preStop() { this.sensors().set(SERVICE_UP, false); disconnectSensors(); } protected void customStop() { driver.stop(); driver.delete(); } protected void disconnectSensors() { connectedSensors = false; disconnectServiceIsRunning(); disconnectServiceUp(); } protected void disconnectServiceIsRunning() { if (serviceProcessIsRunning != null) { serviceProcessIsRunning.stop(); } sensors().set(SERVICE_PROCESS_IS_RUNNING, null); sensors().remove(SERVICE_PROCESS_IS_RUNNING); } protected void disconnectServiceUp() { if (serviceProcessUp != null) { serviceProcessUp.stop(); } sensors().set(SERVICE_UP, null); sensors().remove(SERVICE_UP); } @Override public void restart() { driver.restart(); } @Override public void destroy() { super.destroy(); disconnectSensors(); driver.delete(); } public void waitForEntityStart() { if (log.isDebugEnabled()) { log.debug("waiting to ensure {} doesn't abort prematurely", this); } Duration startTimeout = getConfig(START_TIMEOUT); boolean isRunningResult; isRunningResult = Repeater.create("Wait until the application is running").until(new Callable<Boolean>() { public Boolean call() { return driver.isRunning(); } }).every(Duration.ONE_SECOND).limitTimeTo(startTimeout).run(); if (!isRunningResult) { String msg = "Software process entity " + this + " did not pass is-running " + "check within the required " + startTimeout; log.warn(msg + " (throwing)"); ServiceStateLogic.setExpectedState(this, Lifecycle.RUNNING); throw new IllegalStateException(msg); } } private void initDriver(CloudFoundryPaasLocation location) { EntityPaasDriver newDriver = doInitDriver(location); if (newDriver == null) { throw new UnsupportedOperationException( "cannot start " + this + " on " + location + ": no driver available"); } driver = newDriver; } private EntityPaasDriver doInitDriver(CloudFoundryPaasLocation location) { if (driver != null) { if ((driver instanceof VanillaPaasApplicationCloudFoundryDriver) && location.equals((driver).getLocation())) { return driver; } else { log.warn("driver/location change is untested for {} at {}; changing driver and continuing", this, location); return newDriver(location); } } else { return newDriver(location); } } private EntityPaasDriver newDriver(CloudFoundryPaasLocation location) { EntityDriverManager entityDriverManager = getManagementContext().getEntityDriverManager(); return (EntityPaasDriver) entityDriverManager.build(this, location); } }