/* * 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.camp.spi; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map; import org.apache.brooklyn.camp.commontypes.RepresentationSkew; import org.apache.brooklyn.util.collections.MutableMap; import org.apache.brooklyn.util.text.Identifiers; import org.apache.brooklyn.util.time.Time; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; /** Superclass of CAMP resource implementation objects. * Typically used to hold common state of implementation objects * and to populate the DTO's used by the REST API. * <p> * These class instances are typically created using the * static {@link #builder()} methods they contain. * The resulting instances are typically immutable, * so where fields can change callers should use a new builder * (or update an underlying data store). * <p> * This class is not meant to be instantiated directly, as * CAMP only uses defined subclasses (ie containing these fields). * It is instantiable for testing. */ public class AbstractResource { public static final String CAMP_TYPE = "Resource"; private String id = Identifiers.makeRandomId(8); private String name; private String type; private String description; private String sourceCode; private Date created = Time.dropMilliseconds(new Date()); private List<String> tags = Collections.emptyList(); private RepresentationSkew representationSkew; private Map<String,Object> customAttributes = new MutableMap<String, Object>(); /** Use {@link #builder()} to create */ protected AbstractResource() {} // getters public String getId() { return id; } public String getName() { return name; } public String getType() { return type; } public String getDescription() { return description; } public String getSourceCode() { return sourceCode; } public Date getCreated() { return created; } public List<String> getTags() { return tags; } public RepresentationSkew getRepresentationSkew() { return representationSkew; } public Map<String, Object> getCustomAttributes() { return MutableMap.copyOf(customAttributes).asUnmodifiable(); } // setters private void setId(String id) { this.id = id; } private void setName(String name) { this.name = name; } private void setDescription(String description) { this.description = description; } private void setSourceCode(String sourceCode) { this.sourceCode = sourceCode; } private void setCreated(Date created) { // precision beyond seconds breaks equals check this.created = Time.dropMilliseconds(created); } private void setTags(List<String> tags) { this.tags = ImmutableList.copyOf(tags); } private void setType(String type) { this.type = type; } private void setRepresentationSkew(RepresentationSkew representationSkew) { this.representationSkew = representationSkew; } public void setCustomAttribute(String key, Object value) { this.customAttributes.put(key, value); } // builder @SuppressWarnings("rawtypes") public static Builder<? extends AbstractResource,? extends Builder> builder() { return new AbstractResource().new AbstractResourceBuilder(CAMP_TYPE); } /** Builder creates the instance up front to avoid repetition of fields in the builder; * but prevents object leakage until build and prevents changes after build, * so effectively immutable. * <p> * Similarly setters in the class are private so those objects are also typically effectively immutable. */ public abstract class Builder<T extends AbstractResource,U extends Builder<T,U>> { private boolean built = false; private String type = null; private boolean initialized = false; protected Builder(String type) { this.type = type; } protected final synchronized void check() { if (built) throw new IllegalStateException("Builder instance from "+this+" cannot be access after build"); if (!initialized) { initialized = true; initialize(); } } protected void initialize() { if (type!=null) type(type); } @SuppressWarnings("unchecked") public synchronized T build() { check(); built = true; return (T) AbstractResource.this; } @SuppressWarnings("unchecked") protected U thisBuilder() { return (U)this; } public U type(String x) { check(); AbstractResource.this.setType(x); return thisBuilder(); } public U id(String x) { check(); AbstractResource.this.setId(x); return thisBuilder(); } public U name(String x) { check(); AbstractResource.this.setName(x); return thisBuilder(); } public U description(String x) { check(); AbstractResource.this.setDescription(x); return thisBuilder(); } public U created(Date x) { check(); AbstractResource.this.setCreated(x); return thisBuilder(); } public U tags(List<String> x) { check(); AbstractResource.this.setTags(x); return thisBuilder(); } public U representationSkew(RepresentationSkew x) { check(); AbstractResource.this.setRepresentationSkew(x); return thisBuilder(); } public U customAttribute(String key, Object value) { check(); AbstractResource.this.setCustomAttribute(key, value); return thisBuilder(); } public U sourceCode(String x) { check(); AbstractResource.this.setSourceCode(x); return thisBuilder(); } // public String type() { return instance().type; } } @VisibleForTesting protected class AbstractResourceBuilder extends Builder<AbstractResource,AbstractResourceBuilder> { protected AbstractResourceBuilder(String type) { super(type); } } @Override public String toString() { return super.toString()+"[id="+getId()+"; type="+getType()+"]"; } }