All Downloads are FREE. Search and download functionalities are using the official Maven repository.

brooklyn.entity.proxying.EntitySpec Maven / Gradle / Ivy

There is a newer version: 0.7.0-M1
Show newest version
package brooklyn.entity.proxying;

import static com.google.common.base.Preconditions.checkNotNull;

import java.io.Serializable;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Nullable;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import brooklyn.config.ConfigKey;
import brooklyn.config.ConfigKey.HasConfigKey;
import brooklyn.entity.Entity;
import brooklyn.management.Task;
import brooklyn.policy.Policy;
import brooklyn.policy.PolicySpec;
import brooklyn.util.exceptions.Exceptions;

import com.google.common.base.Objects;
import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
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 brooklyn.entity.proxying.EntitySpec}.
 * 
 * @param  The type of entity to be created
 * 
 * @author aled
 */
public class EntitySpec implements Serializable {

    private static final long serialVersionUID = -2247153452919128990L;
    
    private static final Logger log = LoggerFactory.getLogger(EntitySpec.class);

    /**
     * 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  EntitySpec create(Class type) {
        return new EntitySpec(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  EntitySpec create(Class type, Class implType) {
        return new EntitySpec(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  EntitySpec create(Map config, Class type) {
        return EntitySpec.create(type).configure(config);
    }
    
    /**
     * Wraps an entity spec so its configuration can be overridden without modifying the 
     * original entity spec.
     */
    public static  EntitySpec create(EntitySpec spec) {
        EntitySpec result = create(spec.getType())
                .displayName(spec.getDisplayName())
                .additionalInterfaces(spec.getAdditionalInterfaces())
                .addInitializers(spec.getInitializers())
                .configure(spec.getConfig())
                .configure(spec.getFlags())
                .policySpecs(spec.getPolicySpecs())
                .policies(spec.getPolicies());
        
        if (spec.getParent() != null) result.parent(spec.getParent());
        if (spec.getImplementation() != null) result.impl(spec.getImplementation());
        
        return result;
    }
    
    public static  EntitySpec newInstance(Class type) {
        return new EntitySpec(type);
    }

    private final Class type;
    private String displayName;
    private Class impl;
    private Entity parent;
    private final Map flags = Maps.newLinkedHashMap();
    private final Map, Object> config = Maps.newLinkedHashMap();
    private final List policies = Lists.newArrayList();
    private final List> policySpecs = Lists.newArrayList();
    private final Set> additionalInterfaces = Sets.newLinkedHashSet();
    private final List entityInitializers = Lists.newArrayList();
    private volatile boolean immutable;
    
    public EntitySpec(Class type) {
        this.type = type;
    }
    
    /**
     * @return The type of the entity
     */
    public Class getType() {
        return type;
    }
    
    /**
     * @return The display name of the entity
     */
    public String getDisplayName() {
        return displayName;
    }
    
    /**
     * @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 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> getAdditionalInterfaces() {
        return additionalInterfaces;
    }

    /** @return {@link EntityInitializer} objects which customize the entity to be created */
    public List getInitializers() {
        return entityInitializers;
    }
    
    /**
     * @return The entity's parent
     */
    public Entity getParent() {
        return parent;
    }
    
    /**
     * @return Read-only construction flags
     * @see SetFromFlag declarations on the entity type
     */
    public Map getFlags() {
        return Collections.unmodifiableMap(flags);
    }
    
    /**
     * @return Read-only configuration values
     */
    public Map, Object> getConfig() {
        return Collections.unmodifiableMap(config);
    }
        
    public List> getPolicySpecs() {
        return policySpecs;
    }
    
    public List getPolicies() {
        return policies;
    }
    
    public EntitySpec displayName(String val) {
        checkMutable();
        displayName = val;
        return this;
    }

    public EntitySpec impl(Class val) {
        checkMutable();
        checkIsImplementation(checkNotNull(val, "impl"));
        checkIsNewStyleImplementation(val);
        impl = val;
        return this;
    }

    public EntitySpec additionalInterfaces(Class... vals) {
        checkMutable();
        for (Class val : vals) {
            additionalInterfaces.add(val);
        }
        return this;
    }

    public EntitySpec additionalInterfaces(Iterable> val) {
        checkMutable();
        additionalInterfaces.addAll(Sets.newLinkedHashSet(val));
        return this;
    }

    public EntitySpec addInitializer(EntityInitializer initializer) {
        checkMutable();
        entityInitializers.add(initializer);
        return this;
    }
        
    public EntitySpec addInitializers(Collection initializer) {
        checkMutable();
        entityInitializers.addAll(initializer);
        return this;
    }

    /** The supplied class must have a public no-arg constructor. */
    public EntitySpec addInitializer(Class initializerType) {
        checkMutable();
        try {
            entityInitializers.add(initializerType.newInstance());
        } catch (Exception e) {
            throw Throwables.propagate(e);
        }
        return this;
    }
        
    public EntitySpec parent(Entity val) {
        checkMutable();
        parent = checkNotNull(val, "parent");
        return this;
    }
    
    public EntitySpec configure(Map val) {
        checkMutable();
        for (Map.Entry entry: val.entrySet()) {
            if (entry.getKey()==null) throw new NullPointerException("Null key not permitted");
            if (entry.getKey() instanceof CharSequence)
                flags.put(entry.getKey().toString(), entry.getValue());
            else if (entry.getKey() instanceof ConfigKey)
                config.put((ConfigKey)entry.getKey(), entry.getValue());
            else if (entry.getKey() instanceof HasConfigKey)
                config.put(((HasConfigKey)entry.getKey()).getConfigKey(), entry.getValue());
            else {
                log.warn("Spec "+this+" ignoring unknown config key "+entry.getKey());
            }
        }
        return this;
    }
    
    public EntitySpec configure(CharSequence key, Object val) {
        checkMutable();
        flags.put(checkNotNull(key, "key").toString(), val);
        return this;
    }
    
    public  EntitySpec configure(ConfigKey key, V val) {
        checkMutable();
        config.put(checkNotNull(key, "key"), val);
        return this;
    }

    public  EntitySpec configure(ConfigKey key, Task val) {
        checkMutable();
        config.put(checkNotNull(key, "key"), val);
        return this;
    }

    public  EntitySpec configure(HasConfigKey key, V val) {
        checkMutable();
        config.put(checkNotNull(key, "key").getConfigKey(), val);
        return this;
    }

    public  EntitySpec configure(HasConfigKey key, Task val) {
        checkMutable();
        config.put(checkNotNull(key, "key").getConfigKey(), val);
        return this;
    }

    /** adds a policy to the spec */
    public  EntitySpec policy(Policy val) {
        checkMutable();
        policies.add(checkNotNull(val, "policy"));
        return this;
    }

    /** adds a policy to the spec */
    public  EntitySpec policy(PolicySpec val) {
        checkMutable();
        policySpecs.add(checkNotNull(val, "policySpec"));
        return this;
    }

    /** adds the supplied policies to the spec */
    public  EntitySpec policySpecs(Iterable> val) {
        checkMutable();
        policySpecs.addAll(Sets.newLinkedHashSet(checkNotNull(val, "policySpecs")));
        return this;
    }
    

    /** adds the supplied policies to the spec */
    public  EntitySpec policies(Iterable val) {
        checkMutable();
        policies.addAll(Sets.newLinkedHashSet(checkNotNull(val, "policies")));
        return this;
    }
    
    /** "seals" this spec, preventing any future changes */
    public EntitySpec immutable() {
        immutable = true;
        return this;
    }

    @Override
    public String toString() {
        return Objects.toStringHelper(this).add("type", type).toString();
    }
    
    private void checkMutable() {
        if (immutable) throw new IllegalStateException("Cannot modify immutable entity spec "+this);
    }
    
    // TODO Duplicates method in BasicEntityTypeRegistry
    private void checkIsImplementation(Class val) {
        if (!type.isAssignableFrom(val)) throw new IllegalStateException("Implementation "+val+" does not implement "+type);
        if (val.isInterface()) throw new IllegalStateException("Implementation "+val+" is an interface, but must be a non-abstract class");
        if (Modifier.isAbstract(val.getModifiers())) throw new IllegalStateException("Implementation "+val+" is abstract, but must be a non-abstract class");
    }

    // TODO Duplicates method in BasicEntityTypeRegistry, and InternalEntityFactory.isNewStyleEntity
    private void checkIsNewStyleImplementation(Class implClazz) {
        try {
            implClazz.getConstructor(new Class[0]);
        } catch (NoSuchMethodException e) {
            throw new IllegalStateException("Implementation "+implClazz+" must have a no-argument constructor");
        } catch (SecurityException e) {
            throw Exceptions.propagate(e);
        }
        
        if (implClazz.isInterface()) throw new IllegalStateException("Implementation "+implClazz+" is an interface, but must be a non-abstract class");
        if (Modifier.isAbstract(implClazz.getModifiers())) throw new IllegalStateException("Implementation "+implClazz+" is abstract, but must be a non-abstract class");
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy