/* * 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.objs; import java.util.Collections; import java.util.Map; import java.util.Set; import org.apache.brooklyn.api.internal.ApiObjectsFactory; import org.apache.brooklyn.api.mgmt.ManagementContext; import org.apache.brooklyn.core.entity.AbstractEntity; import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal; import org.apache.brooklyn.core.mgmt.rebind.RebindManagerImpl; import org.apache.brooklyn.core.objs.proxy.InternalFactory; import org.apache.brooklyn.core.relations.ByObjectBasicRelationSupport; import org.apache.brooklyn.util.core.config.ConfigBag; import org.apache.brooklyn.util.core.flags.SetFromFlag; import org.apache.brooklyn.util.text.Identifiers; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import com.google.common.collect.Sets; public abstract class AbstractBrooklynObject implements BrooklynObjectInternal { private static final Logger log = LoggerFactory.getLogger(AbstractBrooklynObject.class); private boolean _legacyConstruction; private boolean hasWarnedOfNoManagementContextWhenPersistRequested; @SetFromFlag(value = "id") private String id = Identifiers.makeRandomId(8); private String catalogItemId; /** callers (only in TagSupport) should synchronize on this for all access */ @SetFromFlag(value = "tags") private final Set<Object> tags = Sets.newLinkedHashSet(); @SuppressWarnings({ "rawtypes", "unchecked" }) private final RelationSupportInternal relations = new ByObjectBasicRelationSupport(this, new RelationChangedCallback()); private volatile ManagementContext managementContext; public abstract void setDisplayName(String newName); public AbstractBrooklynObject() { this(Maps.newLinkedHashMap()); } public AbstractBrooklynObject(Map<?, ?> properties) { _legacyConstruction = !InternalFactory.FactoryConstructionTracker.isConstructing(); if (!_legacyConstruction && properties != null && !properties.isEmpty()) { log.warn("Forcing use of deprecated old-style construction for {} because properties were " + "specified ({}); instead use specs (e.g. LocationSpec, EntitySpec, etc)", getClass().getName(), properties); if (log.isDebugEnabled()) log.debug("Source of use of old-style construction", new Throwable("Source of use of old-style construction")); _legacyConstruction = true; } catalogItemId = ApiObjectsFactory.get().getCatalogItemIdFromContext(); // rely on sub-class to call configure(properties), because otherwise its fields will not have been initialised } /** * See {@link #configure(Map)} * * @deprecated since 0.7.0; only used for legacy brooklyn types where constructor is called directly */ @Deprecated protected BrooklynObjectInternal configure() { return configure(Collections.emptyMap()); } /** * Will set fields from flags, and put the remaining ones into the 'leftovers' map. * For some types, you can find unused config via {@link ConfigBag#getUnusedConfig()}. * <p> * To be overridden by AbstractEntity, AbstractLoation, AbstractPolicy, AbstractEnricher, etc. * <p> * But should not be overridden by specific entity types. If you do, the entity may break in * subsequent releases. Also note that if you require fields to be initialized you must do that * in this method. You must *not* rely on field initializers because they may not run until *after* * this method (this method is invoked by the constructor in this class, so initializers * in subclasses will not have run when this overridden method is invoked.) * * @deprecated since 0.7.0; only used for legacy brooklyn types where constructor is called directly */ @Deprecated protected abstract BrooklynObjectInternal configure(Map<?, ?> flags); protected boolean isLegacyConstruction() { return _legacyConstruction; } /** * Called by framework (in new-style instances where spec was used) after configuring etc, * but before a reference to this instance is shared. * <p> * To preserve backwards compatibility for if the instance is constructed directly, one * can call the code below, but that means it will be called after references to this * policy have been shared with other entities. * <pre> * {@code * if (isLegacyConstruction()) { * init(); * } * } * </pre> */ public void init() { // no-op } /** * Called by framework on rebind (in new-style instances): * <ul> * <li> after configuring, but * <li> before the instance is managed, and * <li> before adjuncts are attached to entities, and * <li> before a reference to an object is shared. * </ul> * Note that {@link #init()} will not be called on rebind. * <p> * If you need to intercept behaviour <i>after</i> adjuncts are attached, * consider {@link AbstractEntity#onManagementStarting()} * (but probably worth raising a discussion on the mailing list!) */ public void rebind() { // no-op } public void setManagementContext(ManagementContextInternal managementContext) { this.managementContext = managementContext; } public ManagementContext getManagementContext() { return managementContext; } protected boolean isRebinding() { return RebindManagerImpl.RebindTracker.isRebinding(); } protected void requestPersist() { if (getManagementContext() != null) { getManagementContext().getRebindManager().getChangeListener().onChanged(this); } else { // Might be nice to log this at debug but it gets hit a lot of times as locations // are created and destroyed for the API. It also might not be an error - the // management context might be null if the object is being recreated by persistence. if (log.isTraceEnabled() && !hasWarnedOfNoManagementContextWhenPersistRequested) { log.trace("Cannot fulfil request to persist {} because it has no management context. " + "This warning will not be logged for this object again.", this); hasWarnedOfNoManagementContextWhenPersistRequested = true; } } } @Override public String getId() { return id; } @Override public void setCatalogItemId(String id) { this.catalogItemId = id; } @Override public String getCatalogItemId() { return catalogItemId; } protected void onTagsChanged() { requestPersist(); } @Override public TagSupport tags() { return new BasicTagSupport(); } protected class BasicTagSupport implements TagSupport { @Override public Set<Object> getTags() { synchronized (tags) { return ImmutableSet.copyOf(tags); } } @Override public boolean containsTag(Object tag) { synchronized (tags) { return tags.contains(tag); } } @Override public boolean addTag(Object tag) { boolean result; synchronized (tags) { result = tags.add(tag); } onTagsChanged(); return result; } @Override public boolean addTags(Iterable<?> newTags) { boolean result; synchronized (tags) { result = Iterables.addAll(tags, newTags); } onTagsChanged(); return result; } @Override public boolean removeTag(Object tag) { boolean result; synchronized (tags) { result = tags.remove(tag); } onTagsChanged(); return result; } } // always override to get casting correct @Override public RelationSupportInternal<?> relations() { return relations; } private class RelationChangedCallback implements Runnable { @Override public void run() { requestPersist(); } } }