You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2014/07/04 11:50:46 UTC

[04/45] git commit: import of brooklyncentral/camp-server dependency to apache brooklyn project

import of brooklyncentral/camp-server dependency to apache brooklyn project


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/d2191c9a
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/d2191c9a
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/d2191c9a

Branch: refs/heads/master
Commit: d2191c9acb807c1092645e54fffed8f40f9decad
Parents: 9ca8250
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Tue Jul 1 13:34:20 2014 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Tue Jul 1 13:34:20 2014 +0100

----------------------------------------------------------------------
 camp/camp-base/notes.txt                        |   64 ++
 camp/camp-base/pom.xml                          |   67 ++
 .../brooklyn/camp/AggregatingCampPlatform.java  |  113 ++
 .../io/brooklyn/camp/BasicCampPlatform.java     |  122 ++
 .../java/io/brooklyn/camp/CampPlatform.java     |   58 +
 .../camp/commontypes/RepresentationSkew.java    |    5 +
 .../io/brooklyn/camp/spi/AbstractResource.java  |  176 +++
 .../brooklyn/camp/spi/ApplicationComponent.java |   78 ++
 .../camp/spi/ApplicationComponentTemplate.java  |   39 +
 .../java/io/brooklyn/camp/spi/Assembly.java     |   94 ++
 .../io/brooklyn/camp/spi/AssemblyTemplate.java  |  102 ++
 .../main/java/io/brooklyn/camp/spi/Link.java    |   22 +
 .../io/brooklyn/camp/spi/PlatformComponent.java |   86 ++
 .../camp/spi/PlatformComponentTemplate.java     |   37 +
 .../brooklyn/camp/spi/PlatformRootSummary.java  |   55 +
 .../brooklyn/camp/spi/PlatformTransaction.java  |   28 +
 .../spi/collection/AbstractResourceLookup.java  |   17 +
 .../collection/AggregatingResourceLookup.java   |   38 +
 .../spi/collection/BasicResourceLookup.java     |   53 +
 .../camp/spi/collection/ResolvableLink.java     |   19 +
 .../camp/spi/collection/ResourceLookup.java     |   29 +
 .../AssemblyTemplateInstantiator.java           |   12 +
 .../BasicAssemblyTemplateInstantiator.java      |   18 +
 .../java/io/brooklyn/camp/spi/pdp/Artifact.java |   82 ++
 .../brooklyn/camp/spi/pdp/ArtifactContent.java  |   47 +
 .../camp/spi/pdp/ArtifactRequirement.java       |   55 +
 .../spi/pdp/AssemblyTemplateConstructor.java    |   76 ++
 .../brooklyn/camp/spi/pdp/DeploymentPlan.java   |   93 ++
 .../java/io/brooklyn/camp/spi/pdp/Service.java  |   77 ++
 .../camp/spi/pdp/ServiceCharacteristic.java     |   55 +
 .../brooklyn/camp/spi/resolve/PdpMatcher.java   |   33 +
 .../brooklyn/camp/spi/resolve/PdpProcessor.java |  154 +++
 .../camp/spi/resolve/PlanInterpreter.java       |   95 ++
 .../interpret/PlanInterpretationContext.java    |  134 +++
 .../interpret/PlanInterpretationNode.java       |  245 ++++
 .../main/java/io/brooklyn/util/yaml/Yamls.java  |   89 ++
 .../src/main/resources/logback-custom.xml       |    9 +
 .../pdp/DeploymentPlanToyInterpreterTest.java   |   96 ++
 .../io/brooklyn/camp/spi/pdp/PdpYamlTest.java   |   60 +
 .../web/MockAssemblyTemplateInstantiator.java   |   20 +
 .../camp/test/mock/web/MockWebPlatform.java     |   71 ++
 .../test/platform/BasicCampPlatformTest.java    |   69 ++
 .../camp/spi/pdp/pdp-single-artifact.yaml       |    9 +
 .../camp/spi/pdp/pdp-single-service.yaml        |   11 +
 .../pdp/yaml-sample-toy-interpreter-result.yaml |    4 +
 .../spi/pdp/yaml-sample-toy-interpreter.yaml    |   10 +
 camp/camp-server/pom.xml                        |  115 ++
 .../io/brooklyn/camp/CampRestResources.java     |   52 +
 .../main/java/io/brooklyn/camp/CampServer.java  |  167 +++
 .../java/io/brooklyn/camp/dto/ApiErrorDto.java  |  101 ++
 .../camp/dto/ApplicationComponentDto.java       |   54 +
 .../dto/ApplicationComponentTemplateDto.java    |   26 +
 .../java/io/brooklyn/camp/dto/AssemblyDto.java  |   59 +
 .../brooklyn/camp/dto/AssemblyTemplateDto.java  |   54 +
 .../main/java/io/brooklyn/camp/dto/DtoBase.java |   13 +
 .../brooklyn/camp/dto/DtoCustomAttributes.java  |   48 +
 .../main/java/io/brooklyn/camp/dto/LinkDto.java |   54 +
 .../brooklyn/camp/dto/PlatformComponentDto.java |   64 ++
 .../camp/dto/PlatformComponentTemplateDto.java  |   26 +
 .../java/io/brooklyn/camp/dto/PlatformDto.java  |  113 ++
 .../java/io/brooklyn/camp/dto/ResourceDto.java  |   94 ++
 .../rest/resource/AbstractCampRestResource.java |   38 +
 .../camp/rest/resource/ApidocRestResource.java  |   13 +
 .../ApplicationComponentRestResource.java       |   32 +
 ...pplicationComponentTemplateRestResource.java |   32 +
 .../rest/resource/AssemblyRestResource.java     |   34 +
 .../resource/AssemblyTemplateRestResource.java  |   70 ++
 .../resource/PlatformComponentRestResource.java |   32 +
 .../PlatformComponentTemplateRestResource.java  |   32 +
 .../rest/resource/PlatformRestResource.java     |   71 ++
 .../io/brooklyn/camp/rest/util/CampJsons.java   |   21 +
 .../camp/rest/util/CampRestContext.java         |   32 +
 .../brooklyn/camp/rest/util/CampRestGuavas.java |   14 +
 .../io/brooklyn/camp/rest/util/DtoFactory.java  |  158 +++
 .../camp/rest/util/WebResourceUtils.java        |   42 +
 .../ApplicationCompomentTemplateDtoTest.java    |   31 +
 .../java/io/brooklyn/camp/dto/BasicDtoTest.java |   72 ++
 .../java/io/brooklyn/camp/dto/LinkDtoTest.java  |   44 +
 .../dto/PlatformCompomentTemplateDtoTest.java   |   31 +
 .../io/brooklyn/camp/dto/ResourceDtoTest.java   |   59 +
 .../rest/resource/PlatformRestResourceTest.java |   24 +
 .../test/fixture/AbstractRestResourceTest.java  |   68 ++
 .../camp/test/fixture/InMemoryCamp.java         |   35 +
 camp/pom.xml                                    | 1060 ++++++++++++++++++
 84 files changed, 6011 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-base/notes.txt
----------------------------------------------------------------------
diff --git a/camp/camp-base/notes.txt b/camp/camp-base/notes.txt
new file mode 100644
index 0000000..7be3ba6
--- /dev/null
+++ b/camp/camp-base/notes.txt
@@ -0,0 +1,64 @@
+
+DONE
+
+SOME of these (for PCT's and ACT's)
+* dto classes and endpoints (jersey resources) to have a rest server
+* impl classes (abstract or composable?) which can make dto's
+* connect impl classes to brooklyn
+
+TODO
+
+* remaining classes
+* PDP formation
+
+
+
+
+
+
+THOUGHTS
+
+* get camp.io
+* project for camp-ops, camp-samp-le-server
+
+
+
+
+
+
+COMPARISON
+
+CAMP
+*   platform offers PlatformComponentTemplates (e.g. elasttic AppServer cluster) which a user can
+    stitch together in an AssemblyTemplate to create an Assembly which is their application;
+    user can also supply ApplicationComponentTemplate and ApplicationComponent instances
+    (e.g. WAR files) for use in assemblies;
+    and Requirements and Capabilities can be defined to e.g. indicate that a 3-tier app
+    Template requires a WAR file and a schema (from the user) and PlatformComponents
+    where these will run (from the platform) which get resolved at real-time 
+    with as little or as much guidance from the user as desired;
+    relies on conventions on types and tags (ie not specified in spec, yet) to facilitate re-use;
+*   defines REST API for interacting with these, and uploading assemblies and app components
+    (not platform components)
+*   example
+
+TOSCA
+    ServiceTemplate is the basic idea
+    Types
+        NodeType
+        RelationshipType
+    TopologyTemplate
+        NodeTemplate
+        RelationshipTemplate
+        GroupTemplate
+    internal elements
+        Artifacts:
+            "deployment artifacts" for creating nodes, 
+            "implementation artifacts" for operations)
+        Container capabilities / requirements
+        Properties, operations, scripts, constraints
+        Policies
+    Plans
+        Plan
+
+END

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-base/pom.xml
----------------------------------------------------------------------
diff --git a/camp/camp-base/pom.xml b/camp/camp-base/pom.xml
new file mode 100644
index 0000000..678fa69
--- /dev/null
+++ b/camp/camp-base/pom.xml
@@ -0,0 +1,67 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <packaging>bundle</packaging>
+
+    <artifactId>camp-base</artifactId>
+
+    <name>CAMP Base</name>
+    <description>
+        Core base classes for CAMP server implementation
+    </description>
+
+    <parent>
+        <groupId>io.brooklyn.camp</groupId>
+        <artifactId>camp-parent</artifactId>
+        <version>0.1.0-SNAPSHOT</version>  <!-- CAMP_IMPL_VERSION -->
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>io.brooklyn</groupId>
+            <artifactId>brooklyn-utils-common</artifactId>
+            <version>${brooklyn.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.brooklyn</groupId>
+            <artifactId>brooklyn-test-support</artifactId>
+            <version>${brooklyn.version}</version>
+            <scope>tests</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <version>${commons-lang3.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-compress</artifactId>
+            <version>${commons-compress.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.yaml</groupId>
+            <artifactId>snakeyaml</artifactId>
+            <version>1.11</version>
+        </dependency>
+        
+            <!-- just for logging, not exported -->
+            <!-- 
+        <dependency>
+            <groupId>io.brooklyn</groupId>
+            <artifactId>brooklyn-logback-xml</artifactId>
+            <version>${brooklyn.version}</version>
+            <optional>true</optional>
+            <scope>tests</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.brooklyn</groupId>
+            <artifactId>brooklyn-core</artifactId>
+            <version>${brooklyn.version}</version>
+            <optional>true</optional>
+            <scope>tests</scope>
+        </dependency>
+             -->
+        
+    </dependencies>
+
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-base/src/main/java/io/brooklyn/camp/AggregatingCampPlatform.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/AggregatingCampPlatform.java b/camp/camp-base/src/main/java/io/brooklyn/camp/AggregatingCampPlatform.java
new file mode 100644
index 0000000..a6caa73
--- /dev/null
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/AggregatingCampPlatform.java
@@ -0,0 +1,113 @@
+package io.brooklyn.camp;
+
+import io.brooklyn.camp.spi.AbstractResource;
+import io.brooklyn.camp.spi.ApplicationComponent;
+import io.brooklyn.camp.spi.ApplicationComponentTemplate;
+import io.brooklyn.camp.spi.Assembly;
+import io.brooklyn.camp.spi.AssemblyTemplate;
+import io.brooklyn.camp.spi.PlatformComponent;
+import io.brooklyn.camp.spi.PlatformComponentTemplate;
+import io.brooklyn.camp.spi.PlatformRootSummary;
+import io.brooklyn.camp.spi.PlatformTransaction;
+import io.brooklyn.camp.spi.collection.AggregatingResourceLookup;
+import io.brooklyn.camp.spi.collection.ResourceLookup;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Function;
+
+/** A {@link CampPlatform} implementation which is empty but allows adding new items,
+ * as well as adding other platforms; meant for subclassing only */
+public class AggregatingCampPlatform extends CampPlatform {
+
+    private static final Logger log = LoggerFactory.getLogger(AggregatingCampPlatform.class);
+    
+    protected AggregatingCampPlatform(PlatformRootSummary root) {
+        this(root, new BasicCampPlatform(root));
+    }
+    
+    public AggregatingCampPlatform(PlatformRootSummary root, CampPlatform platformWhereTransactionsOccur) {
+        super(root);
+        log.debug("Creating {} with main platform: {}", this, platformWhereTransactionsOccur);
+        this.mainPlatform = platformWhereTransactionsOccur;
+    }
+    
+    /** platform where additions are made */
+    CampPlatform mainPlatform;
+    List<CampPlatform> otherPlatformsToSearch = new ArrayList<CampPlatform>();
+    
+    protected void addPlatform(CampPlatform platform) {
+        log.debug("Adding child platform to {}: {}", this, platform);
+        otherPlatformsToSearch.add(platform);
+    }
+    
+    protected <T extends AbstractResource> ResourceLookup<T> aggregatingLookup(Function<CampPlatform, ResourceLookup<T>> lookupFunction) {
+        List<ResourceLookup<T>> lookups = new ArrayList<ResourceLookup<T>>();
+        lookups.add(lookupFunction.apply(mainPlatform));
+        for (CampPlatform p: otherPlatformsToSearch)
+            lookups.add(lookupFunction.apply(p));
+        return AggregatingResourceLookup.of(lookups);
+    }
+    
+    public ResourceLookup<PlatformComponentTemplate> platformComponentTemplates() {
+        return aggregatingLookup(new Function<CampPlatform, ResourceLookup<PlatformComponentTemplate>>() {
+            public ResourceLookup<PlatformComponentTemplate> apply(@Nullable CampPlatform input) {
+                return input.platformComponentTemplates();
+            }
+        });
+    }
+
+    @Override
+    public ResourceLookup<ApplicationComponentTemplate> applicationComponentTemplates() {
+        return aggregatingLookup(new Function<CampPlatform, ResourceLookup<ApplicationComponentTemplate>>() {
+            public ResourceLookup<ApplicationComponentTemplate> apply(@Nullable CampPlatform input) {
+                return input.applicationComponentTemplates();
+            }
+        });
+    }
+
+    public ResourceLookup<AssemblyTemplate> assemblyTemplates() {
+        return aggregatingLookup(new Function<CampPlatform, ResourceLookup<AssemblyTemplate>>() {
+            public ResourceLookup<AssemblyTemplate> apply(@Nullable CampPlatform input) {
+                return input.assemblyTemplates();
+            }
+        });
+    }
+    
+    public ResourceLookup<PlatformComponent> platformComponents() {
+        return aggregatingLookup(new Function<CampPlatform, ResourceLookup<PlatformComponent>>() {
+            public ResourceLookup<PlatformComponent> apply(@Nullable CampPlatform input) {
+                return input.platformComponents();
+            }
+        });
+    }
+
+    @Override
+    public ResourceLookup<ApplicationComponent> applicationComponents() {
+        return aggregatingLookup(new Function<CampPlatform, ResourceLookup<ApplicationComponent>>() {
+            public ResourceLookup<ApplicationComponent> apply(@Nullable CampPlatform input) {
+                return input.applicationComponents();
+            }
+        });
+    }
+
+    public ResourceLookup<Assembly> assemblies() {
+        return aggregatingLookup(new Function<CampPlatform, ResourceLookup<Assembly>>() {
+            public ResourceLookup<Assembly> apply(@Nullable CampPlatform input) {
+                return input.assemblies();
+            }
+        });
+    }
+    
+    @Override
+    public PlatformTransaction transaction() {
+        return mainPlatform.transaction();
+    }
+        
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-base/src/main/java/io/brooklyn/camp/BasicCampPlatform.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/BasicCampPlatform.java b/camp/camp-base/src/main/java/io/brooklyn/camp/BasicCampPlatform.java
new file mode 100644
index 0000000..0214c84
--- /dev/null
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/BasicCampPlatform.java
@@ -0,0 +1,122 @@
+package io.brooklyn.camp;
+
+import io.brooklyn.camp.spi.ApplicationComponent;
+import io.brooklyn.camp.spi.ApplicationComponentTemplate;
+import io.brooklyn.camp.spi.Assembly;
+import io.brooklyn.camp.spi.AssemblyTemplate;
+import io.brooklyn.camp.spi.PlatformComponent;
+import io.brooklyn.camp.spi.PlatformComponentTemplate;
+import io.brooklyn.camp.spi.PlatformRootSummary;
+import io.brooklyn.camp.spi.PlatformTransaction;
+import io.brooklyn.camp.spi.collection.BasicResourceLookup;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** A {@link CampPlatform} implementation which is empty but allows adding new items */
+public class BasicCampPlatform extends CampPlatform {
+
+    private static final Logger log = LoggerFactory.getLogger(BasicCampPlatform.class);
+    
+    public BasicCampPlatform() {
+        this(PlatformRootSummary.builder().name("CAMP Platform").build());
+    }
+    
+    public BasicCampPlatform(PlatformRootSummary root) {
+        super(root);
+    }
+
+    BasicResourceLookup<PlatformComponentTemplate> platformComponentTemplates = new BasicResourceLookup<PlatformComponentTemplate>();
+    BasicResourceLookup<ApplicationComponentTemplate> applicationComponentTemplates = new BasicResourceLookup<ApplicationComponentTemplate>();
+    BasicResourceLookup<AssemblyTemplate> assemblyTemplates = new BasicResourceLookup<AssemblyTemplate>();
+
+    BasicResourceLookup<PlatformComponent> platformComponents = new BasicResourceLookup<PlatformComponent>();
+    BasicResourceLookup<ApplicationComponent> applicationComponents = new BasicResourceLookup<ApplicationComponent>();
+    BasicResourceLookup<Assembly> assemblies = new BasicResourceLookup<Assembly>();
+
+    public BasicResourceLookup<PlatformComponentTemplate> platformComponentTemplates() {
+        return platformComponentTemplates;
+    }
+
+    @Override
+    public BasicResourceLookup<ApplicationComponentTemplate> applicationComponentTemplates() {
+        return applicationComponentTemplates;
+    }
+
+    public BasicResourceLookup<AssemblyTemplate> assemblyTemplates() {
+        return assemblyTemplates;
+    }
+    
+    public BasicResourceLookup<PlatformComponent> platformComponents() {
+        return platformComponents;
+    }
+
+    @Override
+    public BasicResourceLookup<ApplicationComponent> applicationComponents() {
+        return applicationComponents;
+    }
+
+    public BasicResourceLookup<Assembly> assemblies() {
+        return assemblies;
+    }
+    
+    @Override
+    public PlatformTransaction transaction() {
+        return new BasicPlatformTransaction(this);
+    }
+    
+    public static class BasicPlatformTransaction extends PlatformTransaction {
+        private final BasicCampPlatform platform;
+        private final AtomicBoolean committed = new AtomicBoolean(false);
+        
+        public BasicPlatformTransaction(BasicCampPlatform platform) {
+            this.platform = platform;
+        }
+        
+        @Override
+        public void commit() {
+            if (committed.getAndSet(true)) 
+                throw new IllegalStateException("transaction being committed multiple times");
+            
+            for (Object o: additions) {
+                if (o instanceof AssemblyTemplate) {
+                    platform.assemblyTemplates.add((AssemblyTemplate) o);
+                    continue;
+                }
+                if (o instanceof PlatformComponentTemplate) {
+                    platform.platformComponentTemplates.add((PlatformComponentTemplate) o);
+                    continue;
+                }
+                if (o instanceof ApplicationComponentTemplate) {
+                    platform.applicationComponentTemplates.add((ApplicationComponentTemplate) o);
+                    continue;
+                }
+                
+                if (o instanceof Assembly) {
+                    platform.assemblies.add((Assembly) o);
+                    continue;
+                }
+                if (o instanceof PlatformComponent) {
+                    platform.platformComponents.add((PlatformComponent) o);
+                    continue;
+                }
+                if (o instanceof ApplicationComponent) {
+                    platform.applicationComponents.add((ApplicationComponent) o);
+                    continue;
+                }
+
+                throw new UnsupportedOperationException("Object "+o+" of type "+o.getClass()+" cannot be added to "+platform);
+            }
+        }
+        
+        @Override
+        protected void finalize() throws Throwable {
+            if (!committed.get())
+                log.warn("transaction "+this+" was never applied");
+            super.finalize();
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-base/src/main/java/io/brooklyn/camp/CampPlatform.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/CampPlatform.java b/camp/camp-base/src/main/java/io/brooklyn/camp/CampPlatform.java
new file mode 100644
index 0000000..39c1751
--- /dev/null
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/CampPlatform.java
@@ -0,0 +1,58 @@
+package io.brooklyn.camp;
+
+import io.brooklyn.camp.spi.ApplicationComponent;
+import io.brooklyn.camp.spi.ApplicationComponentTemplate;
+import io.brooklyn.camp.spi.Assembly;
+import io.brooklyn.camp.spi.AssemblyTemplate;
+import io.brooklyn.camp.spi.PlatformComponent;
+import io.brooklyn.camp.spi.PlatformComponentTemplate;
+import io.brooklyn.camp.spi.PlatformRootSummary;
+import io.brooklyn.camp.spi.PlatformTransaction;
+import io.brooklyn.camp.spi.collection.ResourceLookup;
+import io.brooklyn.camp.spi.resolve.PdpProcessor;
+
+import com.google.common.base.Preconditions;
+
+public abstract class CampPlatform {
+
+    private final PlatformRootSummary root;
+    private final PdpProcessor pdp;
+
+    public CampPlatform(PlatformRootSummary root) {
+        this.root = Preconditions.checkNotNull(root, "root");
+        pdp = createPdpProcessor();
+    }
+
+    // --- root
+    
+    public PlatformRootSummary root() {
+        return root;
+    }
+
+    // --- other aspects
+    
+    public PdpProcessor pdp() {
+        return pdp;
+    }
+
+    
+    // --- required custom implementation hooks
+    
+    public abstract ResourceLookup<PlatformComponentTemplate> platformComponentTemplates();
+    public abstract ResourceLookup<ApplicationComponentTemplate> applicationComponentTemplates();
+    public abstract ResourceLookup<AssemblyTemplate> assemblyTemplates();
+
+    public abstract ResourceLookup<PlatformComponent> platformComponents();
+    public abstract ResourceLookup<ApplicationComponent> applicationComponents();
+    public abstract ResourceLookup<Assembly> assemblies();
+
+    /** returns object where changes to a PDP can be made; note all changes must be committed */
+    public abstract PlatformTransaction transaction();
+
+    // --- optional customisation overrides
+    
+    protected PdpProcessor createPdpProcessor() {
+        return new PdpProcessor(this);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-base/src/main/java/io/brooklyn/camp/commontypes/RepresentationSkew.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/commontypes/RepresentationSkew.java b/camp/camp-base/src/main/java/io/brooklyn/camp/commontypes/RepresentationSkew.java
new file mode 100644
index 0000000..02725ea
--- /dev/null
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/commontypes/RepresentationSkew.java
@@ -0,0 +1,5 @@
+package io.brooklyn.camp.commontypes;
+
+public enum RepresentationSkew {
+    CREATING, NONE, DESTROYING, UNKNOWN
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/AbstractResource.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/AbstractResource.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/AbstractResource.java
new file mode 100644
index 0000000..0fa50d2
--- /dev/null
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/AbstractResource.java
@@ -0,0 +1,176 @@
+package io.brooklyn.camp.spi;
+
+import io.brooklyn.camp.commontypes.RepresentationSkew;
+
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.text.Identifiers;
+import brooklyn.util.time.Time;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+/** 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 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 Date getCreated() {
+        return created;
+    }
+    public List<String> getTags() {
+        return tags;
+    }
+    public RepresentationSkew getRepresentationSkew() {
+        return representationSkew;
+    }
+    public Map<String, Object> getCustomAttributes() {
+        return ImmutableMap.copyOf(customAttributes);
+    }
+    
+    // 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 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 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 static class Builder<T extends AbstractResource,U extends Builder<T,U>> {
+        
+        private boolean built = false;
+        private String type = null;
+        private T instance = null;
+        
+        protected Builder(String type) {
+            this.type = type;
+        }
+        
+        @SuppressWarnings("unchecked")
+        protected T createResource() {
+            return (T) new AbstractResource();
+        }
+        
+        protected synchronized T instance() {
+            if (built) 
+                throw new IllegalStateException("Builder instance from "+this+" cannot be access after build");
+            if (instance==null) {
+                instance = createResource();
+                initialize();
+            }
+            return instance;
+        }
+
+        protected void initialize() {
+            if (type!=null) type(type);
+        }
+        
+        public synchronized T build() {
+            T result = instance();
+            built = true;
+            return result;
+        }
+        
+        @SuppressWarnings("unchecked")
+        protected U thisBuilder() { return (U)this; }
+        
+        public U type(String x) { instance().setType(x); return thisBuilder(); }
+        public U id(String x) { instance().setId(x); return thisBuilder(); }
+        public U name(String x) { instance().setName(x); return thisBuilder(); }
+        public U description(String x) { instance().setDescription(x); return thisBuilder(); }
+        public U created(Date x) { instance().setCreated(x); return thisBuilder(); }
+        public U tags(List<String> x) { instance().setTags(x); return thisBuilder(); }
+        public U representationSkew(RepresentationSkew x) { instance().setRepresentationSkew(x); return thisBuilder(); }
+        public U customAttribute(String key, Object value) { instance().setCustomAttribute(key, value); return thisBuilder(); }
+        
+//        public String type() { return instance().type; }
+    }
+
+    @VisibleForTesting
+    protected static class AbstractResourceBuilder extends Builder<AbstractResource,AbstractResourceBuilder> {
+        protected AbstractResourceBuilder(String type) {
+            super(type);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return super.toString()+"[id="+getId()+"; type="+getType()+"]";
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/ApplicationComponent.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/ApplicationComponent.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/ApplicationComponent.java
new file mode 100644
index 0000000..5e24adb
--- /dev/null
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/ApplicationComponent.java
@@ -0,0 +1,78 @@
+package io.brooklyn.camp.spi;
+
+import io.brooklyn.camp.spi.collection.BasicResourceLookup;
+import io.brooklyn.camp.spi.collection.ResourceLookup;
+import io.brooklyn.camp.spi.collection.ResourceLookup.EmptyResourceLookup;
+
+
+/** Holds the metadata (name, description, etc) for a PCT
+ * as well as fields pointing to behaviour (eg creation of PlatformComponent).
+ * <p>
+ * See {@link AbstractResource} for more general information.
+ */
+public class ApplicationComponent extends AbstractResource {
+
+    public static final String CAMP_TYPE = "ApplicationComponent";
+    static { assert CAMP_TYPE.equals(ApplicationComponent.class.getSimpleName()); }
+    
+    /** Use {@link #builder()} to create */
+    protected ApplicationComponent() {}
+
+    ResourceLookup<ApplicationComponent> applicationComponents;
+    ResourceLookup<PlatformComponent> platformComponents;
+    String externalManagementUri;
+    
+    public ResourceLookup<ApplicationComponent> getApplicationComponents() {
+        return applicationComponents != null ? applicationComponents : new EmptyResourceLookup<ApplicationComponent>();
+    }
+    public ResourceLookup<PlatformComponent> getPlatformComponents() {
+        return platformComponents != null ? platformComponents : new EmptyResourceLookup<PlatformComponent>();
+    }
+
+    private void setApplicationComponents(ResourceLookup<ApplicationComponent> applicationComponents) {
+        this.applicationComponents = applicationComponents;
+    }
+    private void setPlatformComponents(ResourceLookup<PlatformComponent> platformComponents) {
+        this.platformComponents = platformComponents;
+    }
+    
+    // builder
+    
+    public static Builder<? extends ApplicationComponent> builder() {
+        return new Builder<ApplicationComponent>(CAMP_TYPE);
+    }
+    
+    public static class Builder<T extends ApplicationComponent> extends AbstractResource.Builder<T,Builder<T>> {
+        
+        protected Builder(String type) { super(type); }
+
+        public Builder<T> applicationComponentTemplates(ResourceLookup<ApplicationComponent> x) { instance().setApplicationComponents(x); return thisBuilder(); }
+        public Builder<T> platformComponentTemplates(ResourceLookup<PlatformComponent> x) { instance().setPlatformComponents(x); return thisBuilder(); }
+        
+        public synchronized Builder<T> add(ApplicationComponent x) {
+            if (instance().applicationComponents==null) {
+                instance().applicationComponents = new BasicResourceLookup<ApplicationComponent>();
+            }
+            if (!(instance().applicationComponents instanceof BasicResourceLookup)) {
+                throw new IllegalStateException("Cannot add to resource lookup "+instance().applicationComponents);
+            }
+            ((BasicResourceLookup<ApplicationComponent>)instance().applicationComponents).add(x);
+            return thisBuilder();
+        }
+        
+        public synchronized Builder<T> add(PlatformComponent x) {
+            if (instance().platformComponents==null) {
+                instance().platformComponents = new BasicResourceLookup<PlatformComponent>();
+            }
+            if (!(instance().platformComponents instanceof BasicResourceLookup)) {
+                throw new IllegalStateException("Cannot add to resource lookup "+instance().platformComponents);
+            }
+            ((BasicResourceLookup<PlatformComponent>)instance().platformComponents).add(x);
+            return thisBuilder();
+        }
+
+        @SuppressWarnings("unchecked")
+        protected T createResource() { return (T) new ApplicationComponent(); }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/ApplicationComponentTemplate.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/ApplicationComponentTemplate.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/ApplicationComponentTemplate.java
new file mode 100644
index 0000000..36af06a
--- /dev/null
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/ApplicationComponentTemplate.java
@@ -0,0 +1,39 @@
+package io.brooklyn.camp.spi;
+
+
+/** Holds the metadata (name, description, etc) for a PCT
+ * as well as fields pointing to behaviour (eg creation of PlatformComponent).
+ * <p>
+ * See {@link AbstractResource} for more general information.
+ */
+public class ApplicationComponentTemplate extends AbstractResource {
+
+    public static final String CAMP_TYPE = "ApplicationComponentTemplate";
+    static { assert CAMP_TYPE.equals(ApplicationComponentTemplate.class.getSimpleName()); }
+    
+    /** Use {@link #builder()} to create */
+    protected ApplicationComponentTemplate() {}
+
+    
+    // no fields beyond basic resource
+    
+    // TODO platform component templates, maybe other act's too ?
+    
+    
+    // builder
+    
+    public static Builder<? extends ApplicationComponentTemplate> builder() {
+        return new Builder<ApplicationComponentTemplate>(CAMP_TYPE);
+    }
+    
+    public static class Builder<T extends ApplicationComponentTemplate> extends AbstractResource.Builder<T,Builder<T>> {
+        
+        protected Builder(String type) { super(type); }
+        
+        @SuppressWarnings("unchecked")
+        protected T createResource() { return (T) new ApplicationComponentTemplate(); }
+        
+//        public Builder<T> foo(String x) { instance().setFoo(x); return thisBuilder(); }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/Assembly.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/Assembly.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/Assembly.java
new file mode 100644
index 0000000..719e88f
--- /dev/null
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/Assembly.java
@@ -0,0 +1,94 @@
+package io.brooklyn.camp.spi;
+
+import io.brooklyn.camp.spi.collection.BasicResourceLookup;
+import io.brooklyn.camp.spi.collection.ResourceLookup;
+import io.brooklyn.camp.spi.collection.ResourceLookup.EmptyResourceLookup;
+
+
+/** Holds the metadata (name, description, etc) for an AssemblyTemplate
+ * as well as fields pointing to behaviour (eg list of ACT's).
+ * <p>
+ * See {@link AbstractResource} for more general information.
+ */
+public class Assembly extends AbstractResource {
+
+    public static final String CAMP_TYPE = "Assembly";
+    static { assert CAMP_TYPE.equals(Assembly.class.getSimpleName()); }
+    
+    /** Use {@link #builder()} to create */
+    protected Assembly() {}
+
+    AssemblyTemplate assemblyTemplate;
+    ResourceLookup<ApplicationComponent> applicationComponents;
+    ResourceLookup<PlatformComponent> platformComponents;
+    
+    // TODO
+//    "parameterDefinitionUri": URI,
+//    "pdpUri" : URI ?
+                    
+    public AssemblyTemplate getAssemblyTemplate() {
+        return assemblyTemplate;
+    }
+    public ResourceLookup<ApplicationComponent> getApplicationComponents() {
+        return applicationComponents != null ? applicationComponents : new EmptyResourceLookup<ApplicationComponent>();
+    }
+    public ResourceLookup<PlatformComponent> getPlatformComponents() {
+        return platformComponents != null ? platformComponents : new EmptyResourceLookup<PlatformComponent>();
+    }
+    
+    private void setAssemblyTemplate(AssemblyTemplate assemblyTemplate) {
+        this.assemblyTemplate = assemblyTemplate;
+    }
+    private void setApplicationComponents(ResourceLookup<ApplicationComponent> applicationComponents) {
+        this.applicationComponents = applicationComponents;
+    }
+    private void setPlatformComponents(ResourceLookup<PlatformComponent> platformComponents) {
+        this.platformComponents = platformComponents;
+    }
+    
+    // builder
+    
+    public static Builder<? extends Assembly> builder() {
+        return new Builder<Assembly>(CAMP_TYPE);
+    }
+    
+    public static class Builder<T extends Assembly> extends AbstractResource.Builder<T,Builder<T>> {
+        
+        protected Builder(String type) { super(type); }
+        
+        @SuppressWarnings("unchecked")
+        protected T createResource() { return (T) new Assembly(); }
+        
+        public Builder<T> assemblyTemplate(AssemblyTemplate x) { instance().setAssemblyTemplate(x); return thisBuilder(); }
+        public Builder<T> applicationComponentTemplates(ResourceLookup<ApplicationComponent> x) { instance().setApplicationComponents(x); return thisBuilder(); }
+        public Builder<T> platformComponentTemplates(ResourceLookup<PlatformComponent> x) { instance().setPlatformComponents(x); return thisBuilder(); }
+        
+        public synchronized Builder<T> add(ApplicationComponent x) {
+            if (instance().applicationComponents==null) {
+                instance().applicationComponents = new BasicResourceLookup<ApplicationComponent>();
+            }
+            if (!(instance().applicationComponents instanceof BasicResourceLookup)) {
+                throw new IllegalStateException("Cannot add to resource lookup "+instance().applicationComponents);
+            }
+            ((BasicResourceLookup<ApplicationComponent>)instance().applicationComponents).add(x);
+            return thisBuilder();
+        }
+        
+        public synchronized Builder<T> add(PlatformComponent x) {
+            if (instance().platformComponents==null) {
+                instance().platformComponents = new BasicResourceLookup<PlatformComponent>();
+            }
+            if (!(instance().platformComponents instanceof BasicResourceLookup)) {
+                throw new IllegalStateException("Cannot add to resource lookup "+instance().platformComponents);
+            }
+            ((BasicResourceLookup<PlatformComponent>)instance().platformComponents).add(x);
+            return thisBuilder();
+        }
+        
+        @Override
+        public synchronized T build() {
+            return super.build();
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/AssemblyTemplate.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/AssemblyTemplate.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/AssemblyTemplate.java
new file mode 100644
index 0000000..274f2be
--- /dev/null
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/AssemblyTemplate.java
@@ -0,0 +1,102 @@
+package io.brooklyn.camp.spi;
+
+import com.google.common.base.Preconditions;
+
+import io.brooklyn.camp.spi.collection.BasicResourceLookup;
+import io.brooklyn.camp.spi.collection.ResourceLookup;
+import io.brooklyn.camp.spi.collection.ResourceLookup.EmptyResourceLookup;
+import io.brooklyn.camp.spi.instantiate.AssemblyTemplateInstantiator;
+
+
+/** Holds the metadata (name, description, etc) for an AssemblyTemplate
+ * as well as fields pointing to behaviour (eg list of ACT's).
+ * <p>
+ * See {@link AbstractResource} for more general information.
+ */
+public class AssemblyTemplate extends AbstractResource {
+
+    public static final String CAMP_TYPE = "AssemblyTemplate";
+    static { assert CAMP_TYPE.equals(AssemblyTemplate.class.getSimpleName()); }
+    
+    Class<? extends AssemblyTemplateInstantiator> instantiator;
+    ResourceLookup<ApplicationComponentTemplate> applicationComponentTemplates;
+    ResourceLookup<PlatformComponentTemplate> platformComponentTemplates;
+    
+    // TODO
+//    "parameterDefinitionUri": URI,
+//    "pdpUri" : URI ?
+                    
+    /** Use {@link #builder()} to create */
+    protected AssemblyTemplate() {}
+
+    public Class<? extends AssemblyTemplateInstantiator> getInstantiator() {
+        return instantiator;
+    }
+    public ResourceLookup<ApplicationComponentTemplate> getApplicationComponentTemplates() {
+        return applicationComponentTemplates != null ? applicationComponentTemplates : new EmptyResourceLookup<ApplicationComponentTemplate>();
+    }
+    public ResourceLookup<PlatformComponentTemplate> getPlatformComponentTemplates() {
+        return platformComponentTemplates != null ? platformComponentTemplates : new EmptyResourceLookup<PlatformComponentTemplate>();
+    }
+    
+    private void setInstantiator(Class<? extends AssemblyTemplateInstantiator> instantiator) {
+        this.instantiator = instantiator;
+    }
+    private void setApplicationComponentTemplates(ResourceLookup<ApplicationComponentTemplate> applicationComponentTemplates) {
+        this.applicationComponentTemplates = applicationComponentTemplates;
+    }
+    private void setPlatformComponentTemplates(ResourceLookup<PlatformComponentTemplate> platformComponentTemplates) {
+        this.platformComponentTemplates = platformComponentTemplates;
+    }
+    
+    // builder
+    
+    public static Builder<? extends AssemblyTemplate> builder() {
+        return new Builder<AssemblyTemplate>(CAMP_TYPE);
+    }
+    
+    public static class Builder<T extends AssemblyTemplate> extends AbstractResource.Builder<T,Builder<T>> {
+        
+        protected Builder(String type) { super(type); }
+        
+        @SuppressWarnings("unchecked")
+        protected T createResource() { return (T) new AssemblyTemplate(); }
+        
+        public Builder<T> instantiator(Class<? extends AssemblyTemplateInstantiator> x) { instance().setInstantiator(x); return thisBuilder(); }
+        public Builder<T> applicationComponentTemplates(ResourceLookup<ApplicationComponentTemplate> x) { instance().setApplicationComponentTemplates(x); return thisBuilder(); }
+        public Builder<T> platformComponentTemplates(ResourceLookup<PlatformComponentTemplate> x) { instance().setPlatformComponentTemplates(x); return thisBuilder(); }
+
+        /** allows callers to see the partially formed instance when needed, for example to query instantiators;
+         *  could be replaced by specific methods as and when that is preferred */
+        public T peek() { return instance(); }
+        
+        public synchronized Builder<T> add(ApplicationComponentTemplate x) {
+            if (instance().applicationComponentTemplates==null) {
+                instance().applicationComponentTemplates = new BasicResourceLookup<ApplicationComponentTemplate>();
+            }
+            if (!(instance().applicationComponentTemplates instanceof BasicResourceLookup)) {
+                throw new IllegalStateException("Cannot add to resource lookup "+instance().applicationComponentTemplates);
+            }
+            ((BasicResourceLookup<ApplicationComponentTemplate>)instance().applicationComponentTemplates).add(x);
+            return thisBuilder();
+        }
+        
+        public synchronized Builder<T> add(PlatformComponentTemplate x) {
+            if (instance().platformComponentTemplates==null) {
+                instance().platformComponentTemplates = new BasicResourceLookup<PlatformComponentTemplate>();
+            }
+            if (!(instance().platformComponentTemplates instanceof BasicResourceLookup)) {
+                throw new IllegalStateException("Cannot add to resource lookup "+instance().platformComponentTemplates);
+            }
+            ((BasicResourceLookup<PlatformComponentTemplate>)instance().platformComponentTemplates).add(x);
+            return thisBuilder();
+        }
+        
+        @Override
+        public synchronized T build() {
+            Preconditions.checkNotNull(instance().instantiator);
+            return super.build();
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/Link.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/Link.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/Link.java
new file mode 100644
index 0000000..21058a3
--- /dev/null
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/Link.java
@@ -0,0 +1,22 @@
+package io.brooklyn.camp.spi;
+
+public class Link<T> {
+
+    private final String id;
+    private final String name;
+    
+    public Link(String id, String name) {
+        super();
+        this.id = id;
+        this.name = name;
+    }
+
+    public String getId() {
+        return id;
+    }
+    
+    public String getName() {
+        return name;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/PlatformComponent.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/PlatformComponent.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/PlatformComponent.java
new file mode 100644
index 0000000..326e7a9
--- /dev/null
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/PlatformComponent.java
@@ -0,0 +1,86 @@
+package io.brooklyn.camp.spi;
+
+import io.brooklyn.camp.spi.collection.BasicResourceLookup;
+import io.brooklyn.camp.spi.collection.ResourceLookup;
+import io.brooklyn.camp.spi.collection.ResourceLookup.EmptyResourceLookup;
+
+
+/** Holds the metadata (name, description, etc) for a PCT
+ * as well as fields pointing to behaviour (eg creation of PlatformComponent).
+ * <p>
+ * See {@link AbstractResource} for more general information.
+ */
+public class PlatformComponent extends AbstractResource {
+
+    public static final String CAMP_TYPE = "PlatformComponent";
+    static { assert CAMP_TYPE.equals(PlatformComponent.class.getSimpleName()); }
+    
+    /** Use {@link #builder()} to create */
+    protected PlatformComponent() {}
+
+    ResourceLookup<ApplicationComponent> applicationComponents;
+    ResourceLookup<PlatformComponent> platformComponents;
+    String externalManagementUri;
+    
+    public ResourceLookup<ApplicationComponent> getApplicationComponents() {
+        return applicationComponents != null ? applicationComponents : new EmptyResourceLookup<ApplicationComponent>();
+    }
+    public ResourceLookup<PlatformComponent> getPlatformComponents() {
+        return platformComponents != null ? platformComponents : new EmptyResourceLookup<PlatformComponent>();
+    }
+
+    private void setApplicationComponents(ResourceLookup<ApplicationComponent> applicationComponents) {
+        this.applicationComponents = applicationComponents;
+    }
+    private void setPlatformComponents(ResourceLookup<PlatformComponent> platformComponents) {
+        this.platformComponents = platformComponents;
+    }
+    
+    public String getExternalManagementUri() {
+        return externalManagementUri;
+    }
+    private void setExternalManagementUri(String externalManagementUri) {
+        this.externalManagementUri = externalManagementUri;
+    }
+    
+    // builder
+    
+    public static Builder<? extends PlatformComponent> builder() {
+        return new Builder<PlatformComponent>(CAMP_TYPE);
+    }
+    
+    public static class Builder<T extends PlatformComponent> extends AbstractResource.Builder<T,Builder<T>> {
+        
+        protected Builder(String type) { super(type); }
+        
+        public Builder<T> externalManagementUri(String x) { instance().setExternalManagementUri(x); return thisBuilder(); }
+        public Builder<T> applicationComponentTemplates(ResourceLookup<ApplicationComponent> x) { instance().setApplicationComponents(x); return thisBuilder(); }
+        public Builder<T> platformComponentTemplates(ResourceLookup<PlatformComponent> x) { instance().setPlatformComponents(x); return thisBuilder(); }
+        
+        public synchronized Builder<T> add(ApplicationComponent x) {
+            if (instance().applicationComponents==null) {
+                instance().applicationComponents = new BasicResourceLookup<ApplicationComponent>();
+            }
+            if (!(instance().applicationComponents instanceof BasicResourceLookup)) {
+                throw new IllegalStateException("Cannot add to resource lookup "+instance().applicationComponents);
+            }
+            ((BasicResourceLookup<ApplicationComponent>)instance().applicationComponents).add(x);
+            return thisBuilder();
+        }
+        
+        public synchronized Builder<T> add(PlatformComponent x) {
+            if (instance().platformComponents==null) {
+                instance().platformComponents = new BasicResourceLookup<PlatformComponent>();
+            }
+            if (!(instance().platformComponents instanceof BasicResourceLookup)) {
+                throw new IllegalStateException("Cannot add to resource lookup "+instance().platformComponents);
+            }
+            ((BasicResourceLookup<PlatformComponent>)instance().platformComponents).add(x);
+            return thisBuilder();
+        }
+
+        @SuppressWarnings("unchecked")
+        protected T createResource() { return (T) new PlatformComponent(); }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/PlatformComponentTemplate.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/PlatformComponentTemplate.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/PlatformComponentTemplate.java
new file mode 100644
index 0000000..cf8b9e5
--- /dev/null
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/PlatformComponentTemplate.java
@@ -0,0 +1,37 @@
+package io.brooklyn.camp.spi;
+
+
+/** Holds the metadata (name, description, etc) for a PCT
+ * as well as fields pointing to behaviour (eg creation of PlatformComponent).
+ * <p>
+ * See {@link AbstractResource} for more general information.
+ */
+public class PlatformComponentTemplate extends AbstractResource {
+
+    public static final String CAMP_TYPE = "PlatformComponentTemplate";
+    static { assert CAMP_TYPE.equals(PlatformComponentTemplate.class.getSimpleName()); }
+    
+    /** Use {@link #builder()} to create */
+    protected PlatformComponentTemplate() {}
+
+    
+    // no fields beyond basic resource
+    
+    
+    // builder
+    
+    public static Builder<? extends PlatformComponentTemplate> builder() {
+        return new Builder<PlatformComponentTemplate>(CAMP_TYPE);
+    }
+    
+    public static class Builder<T extends PlatformComponentTemplate> extends AbstractResource.Builder<T,Builder<T>> {
+        
+        protected Builder(String type) { super(type); }
+        
+        @SuppressWarnings("unchecked")
+        protected T createResource() { return (T) new PlatformComponentTemplate(); }
+        
+//        public Builder<T> foo(String x) { instance().setFoo(x); return thisBuilder(); }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/PlatformRootSummary.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/PlatformRootSummary.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/PlatformRootSummary.java
new file mode 100644
index 0000000..1ee2fc5
--- /dev/null
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/PlatformRootSummary.java
@@ -0,0 +1,55 @@
+package io.brooklyn.camp.spi;
+
+/** Holds the metadata (name, description, etc) for a CampPlatform.
+ * Required to initialize a CampPlatform.
+ * <p>
+ * See {@link AbstractResource} for more general information.
+ */
+public class PlatformRootSummary extends AbstractResource {
+
+    public static final String CAMP_TYPE = "Platform";
+    
+    /** Use {@link #builder()} to create */
+    protected PlatformRootSummary() {
+    }
+    
+    // no fields beyond basic resource
+    
+    //TODO:
+    
+    // in the DTO
+    
+//    "supportedFormatsUri": URI, 
+//    "extensionsUri": URI,
+//    "typeDefinitionsUri": URI,
+//    "tags": [ String, + ], ?
+//    "specificationVersion": String[], 
+//    "implementationVersion": String, ? 
+//    "assemblyTemplates": [ Link + ], ? 
+//    "assemblies": [ Link + ], ? 
+//    "platformComponentTemplates": [ Link + ], ? 
+//    "platformComponentCapabilities": [Link + ], ? 
+//    "platformComponents": [ Link + ], ?
+
+    
+    // builder
+    
+    public static Builder<? extends PlatformRootSummary> builder() {
+        return new Builder<PlatformRootSummary>(CAMP_TYPE);
+    }
+    
+    public static class Builder<T extends PlatformRootSummary> extends AbstractResource.Builder<T,Builder<T>> {
+        
+        protected Builder(String type) { super(type); }
+        
+        @SuppressWarnings("unchecked")
+        protected T createResource() { return (T) new PlatformRootSummary(); }
+        
+        protected void initialize() {
+            super.initialize();
+            // TODO a better way not to have an ID here (new subclass BasicIdentifiableResource for other BasicResource instances)
+            id("");
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/PlatformTransaction.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/PlatformTransaction.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/PlatformTransaction.java
new file mode 100644
index 0000000..c7f9490
--- /dev/null
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/PlatformTransaction.java
@@ -0,0 +1,28 @@
+package io.brooklyn.camp.spi;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class PlatformTransaction {
+
+    protected List<Object> additions = new ArrayList<Object>();
+    
+    /** apply the transaction */
+    public abstract void commit();
+    
+    public PlatformTransaction add(AssemblyTemplate at) {
+        additions.add(at);
+        return this;
+    }
+
+    public PlatformTransaction add(ApplicationComponentTemplate act) {
+        additions.add(act);
+        return this;
+    }
+
+    public PlatformTransaction add(PlatformComponentTemplate pct) {
+        additions.add(pct);
+        return this;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/collection/AbstractResourceLookup.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/collection/AbstractResourceLookup.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/collection/AbstractResourceLookup.java
new file mode 100644
index 0000000..8abe410
--- /dev/null
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/collection/AbstractResourceLookup.java
@@ -0,0 +1,17 @@
+package io.brooklyn.camp.spi.collection;
+
+import io.brooklyn.camp.spi.AbstractResource;
+
+public abstract class AbstractResourceLookup<T extends AbstractResource> implements ResourceLookup<T> {
+
+    /** convenience for concrete subclasses */
+    protected ResolvableLink<T> newLink(String id, String name) {
+        return new ResolvableLink<T>(id, name, this);
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return links().isEmpty();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/collection/AggregatingResourceLookup.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/collection/AggregatingResourceLookup.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/collection/AggregatingResourceLookup.java
new file mode 100644
index 0000000..00d759a
--- /dev/null
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/collection/AggregatingResourceLookup.java
@@ -0,0 +1,38 @@
+package io.brooklyn.camp.spi.collection;
+
+import io.brooklyn.camp.spi.AbstractResource;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AggregatingResourceLookup<T extends AbstractResource> extends AbstractResourceLookup<T> {
+
+    List<ResourceLookup<T>> targets = new ArrayList<ResourceLookup<T>>();
+    
+    public static <T extends AbstractResource> AggregatingResourceLookup<T> of(ResourceLookup<T> ...targets) {
+        AggregatingResourceLookup<T> result = new AggregatingResourceLookup<T>();
+        for (ResourceLookup<T> item: targets) result.targets.add(item);
+        return result;
+    }
+    
+    public static <T extends AbstractResource> AggregatingResourceLookup<T> of(Iterable<ResourceLookup<T>> targets) {
+        AggregatingResourceLookup<T> result = new AggregatingResourceLookup<T>();
+        for (ResourceLookup<T> item: targets) result.targets.add(item);
+        return result;        
+    }
+
+    public T get(String id) {
+        for (ResourceLookup<T> item: targets) {
+            T result = item.get(id);
+            if (result!=null) return result;
+        }
+        return null;
+    }
+
+    public List<ResolvableLink<T>> links() {
+        List<ResolvableLink<T>> result = new ArrayList<ResolvableLink<T>>();
+        for (ResourceLookup<T> item: targets) result.addAll(item.links());
+        return result;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/collection/BasicResourceLookup.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/collection/BasicResourceLookup.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/collection/BasicResourceLookup.java
new file mode 100644
index 0000000..0621f60
--- /dev/null
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/collection/BasicResourceLookup.java
@@ -0,0 +1,53 @@
+package io.brooklyn.camp.spi.collection;
+
+import io.brooklyn.camp.spi.AbstractResource;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import brooklyn.util.collections.MutableMap;
+
+public class BasicResourceLookup<T extends AbstractResource> extends AbstractResourceLookup<T> {
+
+    Map<String,T> items = new MutableMap<String,T>();
+    Map<String,ResolvableLink<T>> links = new MutableMap<String,ResolvableLink<T>>();
+    
+    public T get(String id) {
+        return items.get(id);
+    }
+
+    public synchronized List<ResolvableLink<T>> links() {
+        return new ArrayList<ResolvableLink<T>>(links.values());
+    }
+
+    public synchronized void add(T item) {
+        T old = items.put(item.getId(), item);
+        if (old!=null) {
+            items.put(old.getId(), old);
+            throw new IllegalStateException("Already contains item for "+item.getId()+": "+old+" (adding "+item+")");
+        }
+        links.put(item.getId(), newLink(item.getId(), item.getName()));
+    }
+    
+    public synchronized void addAll(T... items) {
+        for (T item: items) add(item);
+    }
+    
+    public synchronized T update(T item) {
+        T old = items.put(item.getId(), item);
+        links.put(item.getId(), newLink(item.getId(), item.getName()));
+        return old;
+    }
+    
+    public synchronized boolean remove(String id) {
+        items.remove(id);
+        return links.remove(id)!=null;
+    }
+    
+    public static <T extends AbstractResource> BasicResourceLookup<T> of(T ...items) {
+        BasicResourceLookup<T> result = new BasicResourceLookup<T>();
+        for (T item: items) result.add(item);
+        return result;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/collection/ResolvableLink.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/collection/ResolvableLink.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/collection/ResolvableLink.java
new file mode 100644
index 0000000..0899692
--- /dev/null
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/collection/ResolvableLink.java
@@ -0,0 +1,19 @@
+package io.brooklyn.camp.spi.collection;
+
+import io.brooklyn.camp.spi.AbstractResource;
+import io.brooklyn.camp.spi.Link;
+
+public class ResolvableLink<T extends AbstractResource> extends Link<T> {
+    
+    protected final ResourceLookup<T> provider;
+    
+    public ResolvableLink(String id, String name, ResourceLookup<T> provider) {
+        super(id, name);
+        this.provider = provider;
+    }
+
+    public T resolve() {
+        return provider.get(getId());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/collection/ResourceLookup.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/collection/ResourceLookup.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/collection/ResourceLookup.java
new file mode 100644
index 0000000..577896a
--- /dev/null
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/collection/ResourceLookup.java
@@ -0,0 +1,29 @@
+package io.brooklyn.camp.spi.collection;
+
+import io.brooklyn.camp.spi.AbstractResource;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+public interface ResourceLookup<T extends AbstractResource> {
+
+    public abstract T get(String id);
+    
+    public abstract List<ResolvableLink<T>> links();
+    
+    public abstract boolean isEmpty();
+
+    public static class EmptyResourceLookup<T extends AbstractResource> implements ResourceLookup<T> {
+        public T get(String id) {
+            throw new NoSuchElementException("no resource: "+id);
+        }
+        public List<ResolvableLink<T>> links() {
+            return Collections.emptyList();
+        }
+        public boolean isEmpty() {
+            return links().isEmpty();
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/instantiate/AssemblyTemplateInstantiator.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/instantiate/AssemblyTemplateInstantiator.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/instantiate/AssemblyTemplateInstantiator.java
new file mode 100644
index 0000000..60055dd
--- /dev/null
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/instantiate/AssemblyTemplateInstantiator.java
@@ -0,0 +1,12 @@
+package io.brooklyn.camp.spi.instantiate;
+
+import io.brooklyn.camp.CampPlatform;
+import io.brooklyn.camp.spi.Assembly;
+import io.brooklyn.camp.spi.AssemblyTemplate;
+
+/** instances of this class should have a public no-arg constructor so the class names can be serialized */
+public interface AssemblyTemplateInstantiator {
+
+    public Assembly instantiate(AssemblyTemplate template, CampPlatform platform);
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/instantiate/BasicAssemblyTemplateInstantiator.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/instantiate/BasicAssemblyTemplateInstantiator.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/instantiate/BasicAssemblyTemplateInstantiator.java
new file mode 100644
index 0000000..e598eab
--- /dev/null
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/instantiate/BasicAssemblyTemplateInstantiator.java
@@ -0,0 +1,18 @@
+package io.brooklyn.camp.spi.instantiate;
+
+import io.brooklyn.camp.CampPlatform;
+import io.brooklyn.camp.spi.Assembly;
+import io.brooklyn.camp.spi.AssemblyTemplate;
+
+/** A simple instantiator which simply invokes the component's instantiators in parallel */
+public class BasicAssemblyTemplateInstantiator implements AssemblyTemplateInstantiator {
+
+    public Assembly instantiate(AssemblyTemplate template, CampPlatform platform) {
+        // TODO this should build it based on the components
+//        template.getPlatformComponentTemplates().links().iterator().next().resolve();
+        
+        // platforms should set a bunch of instantiators, or else let the ComponentTemplates do this!
+        throw new UnsupportedOperationException("Basic instantiator not yet supported");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/Artifact.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/Artifact.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/Artifact.java
new file mode 100644
index 0000000..84445c9
--- /dev/null
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/Artifact.java
@@ -0,0 +1,82 @@
+package io.brooklyn.camp.spi.pdp;
+
+import io.brooklyn.util.yaml.Yamls;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+
+import brooklyn.util.collections.MutableMap;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class Artifact {
+
+    String name;
+    String description;
+    String artifactType;
+    
+    ArtifactContent content;
+    List<ArtifactRequirement> requirements;
+    
+    Map<String,Object> customAttributes;
+    
+    @SuppressWarnings("unchecked")
+    public static Artifact of(Map<String, Object> artifact) {
+        Map<String,Object> fields = MutableMap.copyOf(artifact);
+        
+        Artifact result = new Artifact();
+        result.name = (String) fields.remove("name");
+        result.description = (String) fields.remove("description");
+        result.artifactType = (String) (String) Yamls.removeMultinameAttribute(fields, "artifactType", "type");
+        
+        result.content = ArtifactContent.of( fields.remove("content") );
+        
+        result.requirements = new ArrayList<ArtifactRequirement>();
+        Object reqs = fields.remove("requirements");
+        if (reqs instanceof Iterable) {
+            for (Object req: (Iterable<Object>)reqs) {
+                if (req instanceof Map) {
+                    result.requirements.add(ArtifactRequirement.of((Map<String,Object>) req));
+                } else {
+                    throw new IllegalArgumentException("requirement should be a map, not "+req.getClass());
+                }
+            }
+        } else if (reqs!=null) {
+            // TODO "map" short form
+            throw new IllegalArgumentException("artifacts body should be iterable, not "+reqs.getClass());
+        }
+        
+        result.customAttributes = fields;
+        
+        return result;
+    }
+    
+    public String getName() {
+        return name;
+    }
+    public String getDescription() {
+        return description;
+    }
+    public String getArtifactType() {
+        return artifactType;
+    }
+    public ArtifactContent getContent() {
+        return content;
+    }
+    public List<ArtifactRequirement> getRequirements() {
+        return ImmutableList.copyOf(requirements);
+    }
+    public Map<String, Object> getCustomAttributes() {
+        return ImmutableMap.copyOf(customAttributes);
+    }
+    
+    @Override
+    public String toString() {
+        return ToStringBuilder.reflectionToString(this);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/ArtifactContent.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/ArtifactContent.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/ArtifactContent.java
new file mode 100644
index 0000000..8ae97dc
--- /dev/null
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/ArtifactContent.java
@@ -0,0 +1,47 @@
+package io.brooklyn.camp.spi.pdp;
+
+import java.util.Map;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+
+import brooklyn.util.collections.MutableMap;
+
+import com.google.common.collect.ImmutableMap;
+
+public class ArtifactContent {
+
+    String href;
+    Map<String,Object> customAttributes;
+    
+    public static ArtifactContent of(Object spec) {
+        if (spec==null) return null;
+        
+        ArtifactContent result = new ArtifactContent();
+        if (spec instanceof String) {
+            result.href = (String)spec;
+        } else if (spec instanceof Map) {
+            @SuppressWarnings("unchecked")
+            Map<String,Object> attrs = MutableMap.copyOf( (Map<String,Object>) spec );
+            result.href = (String) attrs.remove("href");
+            result.customAttributes = attrs;            
+        } else {
+            throw new IllegalArgumentException("artifact content should be map or string, not "+spec.getClass());
+        }
+        
+        return result;
+    }
+
+    public String getHref() {
+        return href;
+    }
+    
+    public Map<String, Object> getCustomAttributes() {
+        return ImmutableMap.copyOf(customAttributes);
+    }
+
+    @Override
+    public String toString() {
+        return ToStringBuilder.reflectionToString(this);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/ArtifactRequirement.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/ArtifactRequirement.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/ArtifactRequirement.java
new file mode 100644
index 0000000..5a19e5f
--- /dev/null
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/ArtifactRequirement.java
@@ -0,0 +1,55 @@
+package io.brooklyn.camp.spi.pdp;
+
+import io.brooklyn.util.yaml.Yamls;
+
+import java.util.Map;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+
+import brooklyn.util.collections.MutableMap;
+
+import com.google.common.collect.ImmutableMap;
+
+public class ArtifactRequirement {
+
+    String name;
+    String description;
+    String requirementType;
+    
+    Map<String,Object> customAttributes;
+    
+    public static ArtifactRequirement of(Map<String, Object> req) {
+        Map<String,Object> attrs = MutableMap.copyOf(req);
+        
+        ArtifactRequirement result = new ArtifactRequirement();
+        result.name = (String) attrs.remove("name");
+        result.description = (String) attrs.remove("description");
+        result.requirementType = (String) (String) Yamls.removeMultinameAttribute(attrs, "requirementType", "type");
+        
+        // TODO fulfillment
+        
+        result.customAttributes = attrs;
+        
+        return result;
+    }
+
+    public String getName() {
+        return name;
+    }
+    public String getDescription() {
+        return description;
+    }
+    public String getRequirementType() {
+        return requirementType;
+    }
+    
+    public Map<String, Object> getCustomAttributes() {
+        return ImmutableMap.copyOf(customAttributes);
+    }
+    
+    @Override
+    public String toString() {
+        return ToStringBuilder.reflectionToString(this);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/AssemblyTemplateConstructor.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/AssemblyTemplateConstructor.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/AssemblyTemplateConstructor.java
new file mode 100644
index 0000000..1bd582b
--- /dev/null
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/AssemblyTemplateConstructor.java
@@ -0,0 +1,76 @@
+package io.brooklyn.camp.spi.pdp;
+
+import java.util.Map;
+
+import io.brooklyn.camp.CampPlatform;
+import io.brooklyn.camp.spi.ApplicationComponentTemplate;
+import io.brooklyn.camp.spi.AssemblyTemplate;
+import io.brooklyn.camp.spi.AssemblyTemplate.Builder;
+import io.brooklyn.camp.spi.PlatformComponentTemplate;
+import io.brooklyn.camp.spi.PlatformTransaction;
+import io.brooklyn.camp.spi.instantiate.AssemblyTemplateInstantiator;
+
+public class AssemblyTemplateConstructor {
+
+    private final Builder<? extends AssemblyTemplate> builder;
+    private final CampPlatform campPlatform;
+    protected PlatformTransaction transaction;
+
+    public AssemblyTemplateConstructor(CampPlatform campPlatform) {
+        this.campPlatform = campPlatform;
+        this.builder = AssemblyTemplate.builder();
+        this.transaction = this.campPlatform.transaction();
+    }
+    
+    /** records all the templates to the underlying platform */
+    public AssemblyTemplate commit() {
+        checkState();
+        AssemblyTemplate at = builder.build();
+        transaction.add(at).commit();
+        transaction = null;
+        return at;
+    }
+
+    public void name(String name) {
+        checkState();
+        builder.name(name);
+    }
+
+    public void description(String description) {
+        checkState();
+        builder.description(description);
+    }
+
+    public void addCustomAttributes(Map<String, Object> attrs) {
+        for (Map.Entry<String, Object> attr : attrs.entrySet())
+            builder.customAttribute(attr.getKey(), attr.getValue());
+    }
+
+    public void instantiator(Class<? extends AssemblyTemplateInstantiator> instantiator) {
+        checkState();
+        builder.instantiator(instantiator);
+    }
+    
+    public Class<? extends AssemblyTemplateInstantiator> getInstantiator() {
+        checkState();
+        return builder.peek().getInstantiator();
+    }
+    
+    public void add(ApplicationComponentTemplate act) {
+        checkState();
+        builder.add(act);
+        transaction.add(act);
+    }
+
+    public void add(PlatformComponentTemplate pct) {
+        checkState();
+        builder.add(pct);
+        transaction.add(pct);
+    }
+
+    protected void checkState() {
+        if (transaction == null)
+            throw new IllegalStateException("transaction already committed");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/DeploymentPlan.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/DeploymentPlan.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/DeploymentPlan.java
new file mode 100644
index 0000000..e5fb05e
--- /dev/null
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/DeploymentPlan.java
@@ -0,0 +1,93 @@
+package io.brooklyn.camp.spi.pdp;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+
+import brooklyn.util.collections.MutableMap;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class DeploymentPlan {
+
+    String name;
+    String origin;
+    String description;
+    
+    List<Artifact> artifacts;
+    List<Service> services;
+    Map<String,Object> customAttributes;
+
+    @SuppressWarnings("unchecked")
+    public static DeploymentPlan of(Map<String,Object> root) {
+        Map<String,Object> attrs = MutableMap.copyOf(root);
+        
+        DeploymentPlan result = new DeploymentPlan();
+        result.name = (String) attrs.remove("name");
+        result.description = (String) attrs.remove("description");
+        result.origin = (String) attrs.remove("origin");
+        // TODO version
+        
+        result.services = new ArrayList<Service>();
+        Object services = attrs.remove("services");
+        if (services instanceof Iterable) {
+            for (Object service: (Iterable<Object>)services) {
+                if (service instanceof Map) {
+                    result.services.add(Service.of((Map<String,Object>) service));
+                } else {
+                    throw new IllegalArgumentException("service should be map, not "+service.getClass());
+                }
+            }
+        } else if (services!=null) {
+            // TODO "map" short form
+            throw new IllegalArgumentException("artifacts body should be iterable, not "+services.getClass());
+        }
+        
+        result.artifacts = new ArrayList<Artifact>();
+        Object artifacts = attrs.remove("artifacts");
+        if (artifacts instanceof Iterable) {
+            for (Object artifact: (Iterable<Object>)artifacts) {
+                if (artifact instanceof Map) {
+                    result.artifacts.add(Artifact.of((Map<String,Object>) artifact));
+                } else {
+                    throw new IllegalArgumentException("artifact should be map, not "+artifact.getClass());
+                }
+            }
+        } else if (artifacts!=null) {
+            // TODO "map" short form
+            throw new IllegalArgumentException("artifacts body should be iterable, not "+artifacts.getClass());
+        }
+        
+        result.customAttributes = attrs;
+        
+        return result;
+    }
+
+    public String getName() {
+        return name;
+    }
+    public String getDescription() {
+        return description;
+    }
+    public String getOrigin() {
+        return origin;
+    }
+    public List<Artifact> getArtifacts() {
+        return ImmutableList.copyOf(artifacts);
+    }
+    public List<Service> getServices() {
+        return ImmutableList.copyOf(services);
+    }
+    public Map<String, Object> getCustomAttributes() {
+        return ImmutableMap.copyOf(customAttributes);
+    }
+    
+    @Override
+    public String toString() {
+        return ToStringBuilder.reflectionToString(this);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/Service.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/Service.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/Service.java
new file mode 100644
index 0000000..21ff4a4
--- /dev/null
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/Service.java
@@ -0,0 +1,77 @@
+package io.brooklyn.camp.spi.pdp;
+
+import io.brooklyn.util.yaml.Yamls;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+
+import brooklyn.util.collections.MutableMap;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class Service {
+
+    String name;
+    String description;
+    String serviceType;
+    
+    List<ServiceCharacteristic> characteristics;
+    
+    Map<String,Object> customAttributes;
+    
+    @SuppressWarnings("unchecked")
+    public static Service of(Map<String, Object> service) {
+        Map<String,Object> fields = MutableMap.copyOf(service);
+        
+        Service result = new Service();
+        result.name = (String) fields.remove("name");
+        result.description = (String) fields.remove("description");
+        // FIXME _type needed in lots of places
+        result.serviceType = (String) (String) Yamls.removeMultinameAttribute(fields, "service_type", "serviceType", "type");
+        
+        result.characteristics = new ArrayList<ServiceCharacteristic>();
+        Object chars = fields.remove("characteristics");
+        if (chars instanceof Iterable) {
+            for (Object req: (Iterable<Object>)chars) {
+                if (req instanceof Map) {
+                    result.characteristics.add(ServiceCharacteristic.of((Map<String,Object>) req));
+                } else {
+                    throw new IllegalArgumentException("characteristics should be a map, not "+req.getClass());
+                }
+            }
+        } else if (chars!=null) {
+            // TODO "map" short form
+            throw new IllegalArgumentException("services body should be iterable, not "+chars.getClass());
+        }
+        
+        result.customAttributes = fields;
+        
+        return result;
+    }
+    
+    public String getName() {
+        return name;
+    }
+    public String getDescription() {
+        return description;
+    }
+    public String getServiceType() {
+        return serviceType;
+    }
+    public List<ServiceCharacteristic> getCharacteristics() {
+        return ImmutableList.copyOf(characteristics);
+    }
+    public Map<String, Object> getCustomAttributes() {
+        return ImmutableMap.copyOf(customAttributes);
+    }
+    
+    @Override
+    public String toString() {
+        return ToStringBuilder.reflectionToString(this);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/ServiceCharacteristic.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/ServiceCharacteristic.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/ServiceCharacteristic.java
new file mode 100644
index 0000000..97917dd
--- /dev/null
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/ServiceCharacteristic.java
@@ -0,0 +1,55 @@
+package io.brooklyn.camp.spi.pdp;
+
+import io.brooklyn.util.yaml.Yamls;
+
+import java.util.Map;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+
+import brooklyn.util.collections.MutableMap;
+
+import com.google.common.collect.ImmutableMap;
+
+public class ServiceCharacteristic {
+
+    String name;
+    String description;
+    String characteristicType;
+    
+    Map<String,Object> customAttributes;
+    
+    public static ServiceCharacteristic of(Map<String, Object> req) {
+        Map<String,Object> attrs = MutableMap.copyOf(req);
+        
+        ServiceCharacteristic result = new ServiceCharacteristic();
+        result.name = (String) attrs.remove("name");
+        result.description = (String) attrs.remove("description");
+        result.characteristicType = (String) Yamls.removeMultinameAttribute(attrs, "characteristicType", "type");
+        
+        // TODO fulfillment
+        
+        result.customAttributes = attrs;
+        
+        return result;
+    }
+
+    public String getName() {
+        return name;
+    }
+    public String getDescription() {
+        return description;
+    }
+    public String getCharacteristicType() {
+        return characteristicType;
+    }
+    
+    public Map<String, Object> getCustomAttributes() {
+        return ImmutableMap.copyOf(customAttributes);
+    }
+    
+    @Override
+    public String toString() {
+        return ToStringBuilder.reflectionToString(this);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/resolve/PdpMatcher.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/resolve/PdpMatcher.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/resolve/PdpMatcher.java
new file mode 100644
index 0000000..5ecbf72
--- /dev/null
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/resolve/PdpMatcher.java
@@ -0,0 +1,33 @@
+package io.brooklyn.camp.spi.resolve;
+
+import io.brooklyn.camp.spi.pdp.Artifact;
+import io.brooklyn.camp.spi.pdp.AssemblyTemplateConstructor;
+import io.brooklyn.camp.spi.pdp.Service;
+
+/** Matchers build up the AssemblyTemplate by matching against items in the deployment plan */
+public interface PdpMatcher {
+
+    boolean accepts(Object deploymentPlanItem);
+    boolean apply(Object deploymentPlanItem, AssemblyTemplateConstructor atc);
+
+    public abstract class ArtifactMatcher implements PdpMatcher {
+        private String artifactType;
+        public ArtifactMatcher(String artifactType) {
+            this.artifactType = artifactType;
+        }
+        public boolean accepts(Object art) {
+            return (art instanceof Artifact) && this.artifactType.equals( ((Artifact)art).getArtifactType() );
+        }
+    }
+    
+    public abstract class ServiceMatcher implements PdpMatcher {
+        private String serviceType;
+        public ServiceMatcher(String serviceType) {
+            this.serviceType = serviceType;
+        }
+        public boolean accepts(Object svc) {
+            return (svc instanceof Service) && this.serviceType.equals( ((Service)svc).getServiceType() );
+        }
+    }
+
+}