blob: 744022165d8b9d642d0976e871f8e09c467aa594 [file] [log] [blame]
/*
* 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.api.entity;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
import org.apache.brooklyn.api.internal.AbstractBrooklynObjectSpec;
import org.apache.brooklyn.api.location.Location;
import org.apache.brooklyn.api.policy.Policy;
import org.apache.brooklyn.api.policy.PolicySpec;
import org.apache.brooklyn.api.sensor.Enricher;
import org.apache.brooklyn.api.sensor.EnricherSpec;
import org.apache.brooklyn.util.collections.MutableList;
import com.google.common.base.Function;
import com.google.common.base.Throwables;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
/**
* Gives details of an entity to be created. It describes the entity's configuration, and is
* reusable to create multiple entities with the same configuration.
*
* To create an EntitySpec, it is strongly encouraged to use {@link #create(Class)} etc.
* Users who need to implement this are strongly encouraged to extend
* {@link org.apache.brooklyn.api.entity.EntitySpec}.
*
* @param <T> The type of entity to be created
*
* @author aled
*/
public class EntitySpec<T extends Entity> extends AbstractBrooklynObjectSpec<T,EntitySpec<T>> {
private static final long serialVersionUID = -2247153452919128990L;
/**
* Creates a new {@link EntitySpec} instance for an entity of the given type. The returned
* {@link EntitySpec} can then be customized.
*
* @param type An {@link Entity} interface
*/
public static <T extends Entity> EntitySpec<T> create(Class<T> type) {
return new EntitySpec<T>(type);
}
/**
* Creates a new {@link EntitySpec} instance for an entity of the given type. The returned
* {@link EntitySpec} can then be customized.
*
* @param type An {@link Entity} interface
* @param implType An {@link Entity} implementation, which implements the {@code type} interface
*/
public static <T extends Entity, U extends T> EntitySpec<T> create(Class<T> type, Class<U> implType) {
return new EntitySpec<T>(type).impl(implType);
}
/**
* Creates a new {@link EntitySpec} instance with the given config, for an entity of the given type.
*
* This is primarily for groovy code; equivalent to {@code EntitySpec.create(type).configure(config)}.
*
* @param config The spec's configuration (see {@link EntitySpec#configure(Map)}).
* @param type An {@link Entity} interface
*/
public static <T extends Entity> EntitySpec<T> create(Map<?,?> config, Class<T> type) {
return EntitySpec.create(type).configure(config);
}
/**
* Copies entity spec so its configuration can be overridden without modifying the
* original entity spec.
*/
public static <T extends Entity> EntitySpec<T> create(EntitySpec<T> spec) {
return create(spec.getType()).copyFrom(spec);
}
public static <T extends Entity> EntitySpec<T> newInstance(Class<T> type) {
return new EntitySpec<T>(type);
}
private Class<? extends T> impl;
private Entity parent;
private final List<Policy> policies = Lists.newArrayList();
private final List<PolicySpec<?>> policySpecs = Lists.newArrayList();
private final List<Enricher> enrichers = Lists.newArrayList();
private final List<EnricherSpec<?>> enricherSpecs = Lists.newArrayList();
private final List<Location> locations = Lists.newArrayList();
private final Set<Class<?>> additionalInterfaces = Sets.newLinkedHashSet();
private final List<EntityInitializer> entityInitializers = Lists.newArrayList();
private final List<EntitySpec<?>> children = Lists.newArrayList();
private final List<Entity> members = Lists.newArrayList();
private final List<Group> groups = Lists.newArrayList();
private volatile boolean immutable;
public EntitySpec(Class<T> type) {
super(type);
}
@Override
protected EntitySpec<T> copyFrom(EntitySpec<T> otherSpec) {
super.copyFrom(otherSpec)
.additionalInterfaces(otherSpec.getAdditionalInterfaces())
.policySpecs(otherSpec.getPolicySpecs())
.policies(otherSpec.getPolicies())
.enricherSpecs(otherSpec.getEnricherSpecs())
.enrichers(otherSpec.getEnrichers())
.addInitializers(otherSpec.getInitializers())
.children(copyFromSpecs(otherSpec.getChildren()))
.members(otherSpec.getMembers())
.groups(otherSpec.getGroups())
.locations(otherSpec.getLocations());
if (otherSpec.getParent() != null) parent(otherSpec.getParent());
if (otherSpec.getImplementation() != null) impl(otherSpec.getImplementation());
return this;
}
private List<EntitySpec<?>> copyFromSpecs(List<EntitySpec<?>> children) {
return Lists.<EntitySpec<?>,EntitySpec<?>>transform(children, new Function<EntitySpec<?>, EntitySpec<?>>() {
@Nullable
@Override
public EntitySpec<?> apply(@Nullable EntitySpec<?> entitySpec) {
return create(entitySpec);
}
});
}
@Override
@SuppressWarnings("unchecked")
public Class<T> getType() {
return (Class<T>)super.getType();
}
@Override
protected void checkValidType(Class<? extends T> type) {
// EntitySpec does nothing. Other specs do check it's an implementation etc.
}
/**
* @return The implementation of the entity; if not null. this overrides any defaults or other configuration
*
* @see ImplementedBy on the entity interface classes for how defaults are defined.
* @see EntityTypeRegistry for how implementations can be defined globally
*/
@Nullable
public Class<? extends T> getImplementation() {
return impl;
}
/**
* @return Additional interfaces (other than just {@link #getType()}) that this entity implements;
* important for when accessing entity through a proxy to determine which interfaces the proxy exposes.
*/
public Set<Class<?>> getAdditionalInterfaces() {
return additionalInterfaces;
}
/** @return {@link EntityInitializer} objects which customize the entity to be created */
public List<EntityInitializer> getInitializers() {
return entityInitializers;
}
public List<EntitySpec<?>> getChildren() {
return children;
}
public List<Entity> getMembers() {
return members;
}
public List<Group> getGroups() {
return groups;
}
/**
* @return The entity's parent
*/
public Entity getParent() {
return parent;
}
public List<PolicySpec<?>> getPolicySpecs() {
return policySpecs;
}
public List<Policy> getPolicies() {
return policies;
}
public List<EnricherSpec<?>> getEnricherSpecs() {
return enricherSpecs;
}
public List<Enricher> getEnrichers() {
return enrichers;
}
public List<Location> getLocations() {
return locations;
}
public EntitySpec<T> impl(Class<? extends T> val) {
checkMutable();
checkIsImplementation(checkNotNull(val, "impl"), getType());
checkIsNewStyleImplementation(val);
impl = val;
return this;
}
public EntitySpec<T> additionalInterfaces(Class<?>... vals) {
checkMutable();
for (Class<?> val : vals) {
additionalInterfaces.add(val);
}
return this;
}
public EntitySpec<T> additionalInterfaces(Iterable<Class<?>> val) {
checkMutable();
additionalInterfaces.addAll(Sets.newLinkedHashSet(val));
return this;
}
public EntitySpec<T> addInitializer(EntityInitializer initializer) {
checkMutable();
entityInitializers.add(initializer);
return this;
}
public EntitySpec<T> addInitializers(Iterable<? extends EntityInitializer> initializers) {
checkMutable();
Iterables.addAll(entityInitializers, initializers);
return this;
}
/** The supplied class must have a public no-arg constructor. */
public EntitySpec<T> addInitializer(Class<? extends EntityInitializer> initializerType) {
checkMutable();
try {
entityInitializers.add(initializerType.newInstance());
} catch (Exception e) {
throw Throwables.propagate(e);
}
return this;
}
public EntitySpec<T> children(Iterable<? extends EntitySpec<?>> children) {
checkMutable();
Iterables.addAll(this.children, children);
return this;
}
/** The supplied class must have a public no-arg constructor. */
public EntitySpec<T> child(EntitySpec<?> child) {
checkMutable();
children.add(child);
return this;
}
public EntitySpec<T> members(Iterable<? extends Entity> members) {
checkMutable();
Iterables.addAll(this.members, members);
return this;
}
public EntitySpec<T> member(Entity member) {
checkMutable();
members.add(member);
return this;
}
public EntitySpec<T> groups(Iterable<? extends Group> groups) {
checkMutable();
Iterables.addAll(this.groups, groups);
return this;
}
public EntitySpec<T> group(Group group) {
checkMutable();
groups.add(group);
return this;
}
public EntitySpec<T> parent(Entity val) {
checkMutable();
parent = checkNotNull(val, "parent");
return this;
}
/** adds a policy to the spec */
public <V> EntitySpec<T> policy(Policy val) {
checkMutable();
policies.add(checkNotNull(val, "policy"));
return this;
}
/** adds a policy to the spec */
public <V> EntitySpec<T> policy(PolicySpec<?> val) {
checkMutable();
policySpecs.add(checkNotNull(val, "policySpec"));
return this;
}
/** adds the supplied policies to the spec */
public <V> EntitySpec<T> policySpecs(Iterable<? extends PolicySpec<?>> val) {
checkMutable();
policySpecs.addAll(MutableList.copyOf(checkNotNull(val, "policySpecs")));
return this;
}
/** adds the supplied policies to the spec */
public <V> EntitySpec<T> policies(Iterable<? extends Policy> val) {
checkMutable();
policies.addAll(MutableList.copyOf(checkNotNull(val, "policies")));
return this;
}
/** adds a policy to the spec */
public <V> EntitySpec<T> enricher(Enricher val) {
checkMutable();
enrichers.add(checkNotNull(val, "enricher"));
return this;
}
/** adds a policy to the spec */
public <V> EntitySpec<T> enricher(EnricherSpec<?> val) {
checkMutable();
enricherSpecs.add(checkNotNull(val, "enricherSpec"));
return this;
}
/** adds the supplied policies to the spec */
public <V> EntitySpec<T> enricherSpecs(Iterable<? extends EnricherSpec<?>> val) {
checkMutable();
enricherSpecs.addAll(MutableList.copyOf(checkNotNull(val, "enricherSpecs")));
return this;
}
/** adds the supplied policies to the spec */
public <V> EntitySpec<T> enrichers(Iterable<? extends Enricher> val) {
checkMutable();
enrichers.addAll(MutableList.copyOf(checkNotNull(val, "enrichers")));
return this;
}
/** adds a location to the spec */
public <V> EntitySpec<T> location(Location val) {
checkMutable();
locations.add(checkNotNull(val, "location"));
return this;
}
/** adds the supplied locations to the spec */
public <V> EntitySpec<T> locations(Iterable<? extends Location> val) {
checkMutable();
locations.addAll(MutableList.copyOf(checkNotNull(val, "locations")));
return this;
}
/** "seals" this spec, preventing any future changes */
public EntitySpec<T> immutable() {
immutable = true;
return this;
}
private void checkMutable() {
if (immutable) throw new IllegalStateException("Cannot modify immutable entity spec "+this);
}
}