/* * 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 static com.google.common.base.Preconditions.checkNotNull; import java.util.Collection; import org.apache.brooklyn.api.entity.EntityLocal; import org.apache.brooklyn.api.mgmt.rebind.RebindSupport; import org.apache.brooklyn.api.mgmt.rebind.mementos.FeedMemento; import org.apache.brooklyn.api.sensor.Feed; import org.apache.brooklyn.config.ConfigKey; import org.apache.brooklyn.core.BrooklynFeatureEnablement; import org.apache.brooklyn.core.config.ConfigKeys; import org.apache.brooklyn.core.entity.EntityInternal; import org.apache.brooklyn.core.mgmt.rebind.BasicFeedRebindSupport; import org.apache.brooklyn.core.objs.AbstractEntityAdjunct; import org.apache.brooklyn.util.javalang.JavaClassNames; import org.apache.brooklyn.util.text.Strings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Captures common fields and processes for sensor feeds. * These generally poll or subscribe to get sensor values for an entity. * They make it easy to poll over http, jmx, etc. */ public abstract class AbstractFeed extends AbstractEntityAdjunct implements Feed { private static final Logger log = LoggerFactory.getLogger(AbstractFeed.class); public static final ConfigKey<Boolean> ONLY_IF_SERVICE_UP = ConfigKeys.newBooleanConfigKey("feed.onlyIfServiceUp", "", false); private final Object pollerStateMutex = new Object(); private transient volatile Poller<?> poller; private transient volatile boolean activated; private transient volatile boolean suspended; public AbstractFeed() { } /** * @deprecated since 0.7.0; use no-arg constructor; call {@link #setEntity(EntityLocal)} */ @Deprecated public AbstractFeed(EntityLocal entity) { this(entity, false); } /** * @deprecated since 0.7.0; use no-arg constructor; call {@link #setEntity(EntityLocal)} and {@code setConfig(ONLY_IF_SERVICE_UP, onlyIfServiceUp)} */ @Deprecated public AbstractFeed(EntityLocal entity, boolean onlyIfServiceUp) { this.entity = checkNotNull(entity, "entity"); setConfig(ONLY_IF_SERVICE_UP, onlyIfServiceUp); } // Ensure idempotent, as called in builders (in case not registered with entity), and also called // when registering with entity @Override public void setEntity(EntityLocal entity) { super.setEntity(entity); if (BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_FEED_REGISTRATION_PROPERTY)) { ((EntityInternal)entity).feeds().addFeed(this); } } protected void initUniqueTag(String uniqueTag, Object ...valsForDefault) { if (Strings.isNonBlank(uniqueTag)) this.uniqueTag = uniqueTag; else this.uniqueTag = getDefaultUniqueTag(valsForDefault); } protected String getDefaultUniqueTag(Object ...valsForDefault) { StringBuilder sb = new StringBuilder(); sb.append(JavaClassNames.simpleClassName(this)); if (valsForDefault.length==0) { sb.append("@"); sb.append(hashCode()); } else if (valsForDefault.length==1 && valsForDefault[0] instanceof Collection){ sb.append(Strings.toUniqueString(valsForDefault[0], 80)); } else { sb.append("["); boolean first = true; for (Object x: valsForDefault) { if (!first) sb.append(";"); else first = false; sb.append(Strings.toUniqueString(x, 80)); } sb.append("]"); } return sb.toString(); } @Override public void start() { if (log.isDebugEnabled()) log.debug("Starting feed {} for {}", this, entity); if (activated) { throw new IllegalStateException(String.format("Attempt to start feed %s of entity %s when already running", this, entity)); } if (poller != null) { throw new IllegalStateException(String.format("Attempt to re-start feed %s of entity %s", this, entity)); } poller = new Poller<Object>(entity, getConfig(ONLY_IF_SERVICE_UP)); activated = true; preStart(); synchronized (pollerStateMutex) { // don't start poller if we are suspended if (!suspended) { poller.start(); } } } @Override public void suspend() { synchronized (pollerStateMutex) { if (activated && !suspended) { poller.stop(); } suspended = true; } } @Override public void resume() { synchronized (pollerStateMutex) { if (activated && suspended) { poller.start(); } suspended = false; } } @Override public void destroy() { stop(); } @Override public void stop() { if (!activated) { log.debug("Ignoring attempt to stop feed {} of entity {} when not running", this, entity); return; } if (log.isDebugEnabled()) log.debug("stopping feed {} for {}", this, entity); activated = false; preStop(); synchronized (pollerStateMutex) { if (!suspended) { poller.stop(); } } postStop(); super.destroy(); } @Override public boolean isActivated() { return activated; } public EntityLocal getEntity() { return entity; } protected boolean isConnected() { // TODO Default impl will result in multiple logs for same error if becomes unreachable // (e.g. if ssh gets NoRouteToHostException, then every AttributePollHandler for that // feed will log.warn - so if polling for 10 sensors/attributes will get 10 log messages). // Would be nice if reduced this logging duplication. // (You can reduce it by providing a better 'isConnected' implementation of course.) return isRunning() && entity!=null && !((EntityInternal)entity).getManagementSupport().isNoLongerManaged(); } @Override public boolean isSuspended() { return suspended; } @Override public boolean isRunning() { return isActivated() && !isSuspended() && !isDestroyed() && getPoller()!=null && getPoller().isRunning(); } @Override public RebindSupport<FeedMemento> getRebindSupport() { return new BasicFeedRebindSupport(this); } @SuppressWarnings("unchecked") @Override public RelationSupportInternal<Feed> relations() { return (RelationSupportInternal<Feed>) super.relations(); } @Override protected void onChanged() { // TODO Auto-generated method stub } /** * For overriding. */ protected void preStart() { } /** * For overriding. */ protected void preStop() { } /** * For overriding. */ protected void postStop() { } /** * For overriding, where sub-class can change return-type generics! */ protected Poller<?> getPoller() { return poller; } }