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:44 UTC

[02/45] import of brooklyncentral/camp-server dependency to apache brooklyn project

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-server/src/main/java/io/brooklyn/camp/dto/PlatformDto.java
----------------------------------------------------------------------
diff --git a/camp/camp-server/src/main/java/io/brooklyn/camp/dto/PlatformDto.java b/camp/camp-server/src/main/java/io/brooklyn/camp/dto/PlatformDto.java
new file mode 100644
index 0000000..f86fd05
--- /dev/null
+++ b/camp/camp-server/src/main/java/io/brooklyn/camp/dto/PlatformDto.java
@@ -0,0 +1,113 @@
+package io.brooklyn.camp.dto;
+
+import io.brooklyn.camp.rest.resource.ApidocRestResource;
+import io.brooklyn.camp.rest.util.DtoFactory;
+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.Link;
+import io.brooklyn.camp.spi.PlatformComponent;
+import io.brooklyn.camp.spi.PlatformComponentTemplate;
+import io.brooklyn.camp.spi.PlatformRootSummary;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+
+public class PlatformDto extends ResourceDto {
+
+    // defined as a constant so can be used in Swagger REST API annotations
+    public static final String CLASS_NAME = "io.brooklyn.camp.dto.PlatformDto";
+    static { assert CLASS_NAME.equals(PlatformDto.class.getCanonicalName()); }
+
+    protected PlatformDto() {}
+    protected PlatformDto(DtoFactory dtoFactory, PlatformRootSummary x) {
+        super(dtoFactory, x);
+        platformComponentTemplates = new ArrayList<LinkDto>();
+        for (Link<PlatformComponentTemplate> t: dtoFactory.getPlatform().platformComponentTemplates().links()) {
+            platformComponentTemplates.add(LinkDto.newInstance(dtoFactory, PlatformComponentTemplate.class, t));
+        }
+        
+        applicationComponentTemplates = new ArrayList<LinkDto>();
+        for (Link<ApplicationComponentTemplate> t: dtoFactory.getPlatform().applicationComponentTemplates().links()) {
+            applicationComponentTemplates.add(LinkDto.newInstance(dtoFactory, ApplicationComponentTemplate.class, t));
+        }
+
+        assemblyTemplates = new ArrayList<LinkDto>();
+        for (Link<AssemblyTemplate> t: dtoFactory.getPlatform().assemblyTemplates().links()) {
+            assemblyTemplates.add(LinkDto.newInstance(dtoFactory, AssemblyTemplate.class, t));
+        }
+
+        platformComponents = new ArrayList<LinkDto>();
+        for (Link<PlatformComponent> t: dtoFactory.getPlatform().platformComponents().links()) {
+            platformComponents.add(LinkDto.newInstance(dtoFactory, PlatformComponent.class, t));
+        }
+        
+        applicationComponents = new ArrayList<LinkDto>();
+        for (Link<ApplicationComponent> t: dtoFactory.getPlatform().applicationComponents().links()) {
+            applicationComponents.add(LinkDto.newInstance(dtoFactory, ApplicationComponent.class, t));
+        }
+
+        assemblies = new ArrayList<LinkDto>();
+        for (Link<Assembly> t: dtoFactory.getPlatform().assemblies().links()) {
+            assemblies.add(LinkDto.newInstance(dtoFactory, Assembly.class, t));
+        }
+
+        // TODO set custom fields
+
+        apidoc = LinkDto.newInstance(
+                dtoFactory.getUriFactory().uriOfRestResource(ApidocRestResource.class),
+                "API documentation");
+    }
+
+    // TODO add custom fields
+    private List<LinkDto> assemblyTemplates;
+    private List<LinkDto> platformComponentTemplates;
+    private List<LinkDto> applicationComponentTemplates;
+    private List<LinkDto> assemblies;
+    private List<LinkDto> platformComponents;
+    private List<LinkDto> applicationComponents;
+    
+    // non-CAMP, but useful
+    private LinkDto apidoc;
+    
+    public List<LinkDto> getAssemblyTemplates() {
+        return assemblyTemplates;
+    }
+    
+    public List<LinkDto> getPlatformComponentTemplates() {
+        return platformComponentTemplates;
+    }
+    
+    public List<LinkDto> getApplicationComponentTemplates() {
+        return applicationComponentTemplates;
+    }
+    
+    public List<LinkDto> getAssemblies() {
+        return assemblies;
+    }
+    
+    @JsonInclude(Include.NON_EMPTY)
+    public List<LinkDto> getPlatformComponents() {
+        return platformComponents;
+    }
+    
+    @JsonInclude(Include.NON_EMPTY)
+    public List<LinkDto> getApplicationComponents() {
+        return applicationComponents;
+    }
+    
+    public LinkDto getApidoc() {
+        return apidoc;
+    }
+    
+    // --- building ---
+
+    public static PlatformDto newInstance(DtoFactory dtoFactory, PlatformRootSummary x) {
+        return new PlatformDto(dtoFactory, x);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-server/src/main/java/io/brooklyn/camp/dto/ResourceDto.java
----------------------------------------------------------------------
diff --git a/camp/camp-server/src/main/java/io/brooklyn/camp/dto/ResourceDto.java b/camp/camp-server/src/main/java/io/brooklyn/camp/dto/ResourceDto.java
new file mode 100644
index 0000000..93caaf1
--- /dev/null
+++ b/camp/camp-server/src/main/java/io/brooklyn/camp/dto/ResourceDto.java
@@ -0,0 +1,94 @@
+package io.brooklyn.camp.dto;
+
+import io.brooklyn.camp.commontypes.RepresentationSkew;
+import io.brooklyn.camp.rest.util.DtoFactory;
+import io.brooklyn.camp.spi.AbstractResource;
+
+import java.util.Date;
+import java.util.List;
+
+import brooklyn.util.time.Time;
+
+import com.fasterxml.jackson.annotation.JsonGetter;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.annotation.JsonSetter;
+import com.fasterxml.jackson.databind.util.ISO8601Utils;
+
+public class ResourceDto extends DtoCustomAttributes {
+
+    protected ResourceDto() {}
+    protected ResourceDto(DtoFactory dtoFactory, AbstractResource x) {
+        type = x.getType();
+        name = x.getName();
+
+        description = x.getDescription();
+        setCreated(x.getCreated());
+        tags = x.getTags();
+        representationSkew = x.getRepresentationSkew();
+        
+        if (x.getCustomAttributes()!=null && !x.getCustomAttributes().isEmpty())
+            newInstanceCustomAttributes(x.getCustomAttributes());
+        
+        uri = dtoFactory.uri(x);
+    }
+    
+    private String uri;
+    private String type;
+    
+    private String name;
+    private String description;
+    private Date created;
+    private List<String> tags;
+    private RepresentationSkew representationSkew;
+
+    public String getUri() {
+        return uri;
+    }
+    
+    public String getName() {
+        return name;
+    }
+    
+    @JsonInclude(Include.NON_NULL)
+    public String getDescription() {
+        return description;
+    }
+    
+    @JsonGetter("created")
+    public String getCreatedAsString() {
+        return created==null ? null : ISO8601Utils.format(created);
+    }
+    
+    @JsonSetter
+    private void setCreated(Date created) {
+        this.created = Time.dropMilliseconds(created);
+    }
+
+    @JsonIgnore
+    public Date getCreated() {
+        return created;
+    }
+    
+    @JsonInclude(Include.NON_EMPTY)
+    public List<String> getTags() {
+        return tags;
+    }
+    
+    public String getType() {
+        return type;
+    }
+    
+    @JsonInclude(Include.NON_NULL)
+    public RepresentationSkew getRepresentationSkew() {
+        return representationSkew;
+    }
+
+    // --- building ---
+
+    public static ResourceDto newInstance(DtoFactory dtoFactory, AbstractResource x) {
+        return new ResourceDto(dtoFactory, x);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/AbstractCampRestResource.java
----------------------------------------------------------------------
diff --git a/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/AbstractCampRestResource.java b/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/AbstractCampRestResource.java
new file mode 100644
index 0000000..735ac39
--- /dev/null
+++ b/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/AbstractCampRestResource.java
@@ -0,0 +1,38 @@
+package io.brooklyn.camp.rest.resource;
+
+import io.brooklyn.camp.CampPlatform;
+import io.brooklyn.camp.rest.util.CampRestContext;
+import io.brooklyn.camp.rest.util.DtoFactory;
+import io.brooklyn.camp.rest.util.WebResourceUtils;
+import io.brooklyn.camp.spi.AbstractResource;
+import io.brooklyn.camp.spi.collection.ResourceLookup;
+
+import javax.servlet.ServletContext;
+import javax.ws.rs.core.Context;
+
+public abstract class AbstractCampRestResource {
+
+    // can be injected by jersey when not injected manually
+    // (seems there is no way to make this optional so note it _must_ be injected; if needed
+    // see notes on workarounds for test frameworks in original AbstractBrooklynRestResource)
+    @Context ServletContext servletContext;
+    
+    private CampRestContext campRestContext;
+    
+    public synchronized CampRestContext context() {
+        if (campRestContext!=null) return campRestContext;
+        campRestContext = new CampRestContext(servletContext);
+        return campRestContext;
+    }
+    
+    public CampPlatform camp() { return context().camp(); }
+    public DtoFactory dto() { return context().dto(); }
+
+    public static <T extends AbstractResource> T lookup(ResourceLookup<T> list, String id) {
+        T result = list.get(id);
+        if (result==null)
+            throw WebResourceUtils.notFound("No such element: %s", id);
+        return result;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/ApidocRestResource.java
----------------------------------------------------------------------
diff --git a/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/ApidocRestResource.java b/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/ApidocRestResource.java
new file mode 100644
index 0000000..53b3311
--- /dev/null
+++ b/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/ApidocRestResource.java
@@ -0,0 +1,13 @@
+package io.brooklyn.camp.rest.resource;
+
+import javax.ws.rs.Path;
+
+import brooklyn.rest.apidoc.Apidoc;
+
+@Path(ApidocRestResource.API_URI_PATH)
+@Apidoc("Web API Documentation")
+public class ApidocRestResource extends brooklyn.rest.apidoc.ApidocResource {
+
+    public static final String API_URI_PATH = PlatformRestResource.CAMP_URI_PATH + "/apidoc";
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/ApplicationComponentRestResource.java
----------------------------------------------------------------------
diff --git a/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/ApplicationComponentRestResource.java b/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/ApplicationComponentRestResource.java
new file mode 100644
index 0000000..7f100fc
--- /dev/null
+++ b/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/ApplicationComponentRestResource.java
@@ -0,0 +1,32 @@
+package io.brooklyn.camp.rest.resource;
+
+import io.brooklyn.camp.dto.ApplicationComponentDto;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+
+import brooklyn.rest.apidoc.Apidoc;
+
+import com.wordnik.swagger.core.ApiOperation;
+import com.wordnik.swagger.core.ApiParam;
+
+@Path(ApplicationComponentRestResource.URI_PATH)
+@Apidoc("Application Component resources")
+@Produces("application/json")
+public class ApplicationComponentRestResource extends AbstractCampRestResource {
+
+    public static final String URI_PATH = PlatformRestResource.CAMP_URI_PATH + "/application-components";
+
+    @Path("/{id}")
+    @ApiOperation(value = "Get a specific application component",
+        responseClass = ApplicationComponentDto.CLASS_NAME)
+    @GET
+    public ApplicationComponentDto get(
+            @ApiParam(value = "ID of item being retrieved", required = true)
+            @PathParam("id") String id) {
+        return dto().adapt(lookup(camp().applicationComponents(), id));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/ApplicationComponentTemplateRestResource.java
----------------------------------------------------------------------
diff --git a/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/ApplicationComponentTemplateRestResource.java b/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/ApplicationComponentTemplateRestResource.java
new file mode 100644
index 0000000..1ab5e19
--- /dev/null
+++ b/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/ApplicationComponentTemplateRestResource.java
@@ -0,0 +1,32 @@
+package io.brooklyn.camp.rest.resource;
+
+import io.brooklyn.camp.dto.ApplicationComponentTemplateDto;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+
+import brooklyn.rest.apidoc.Apidoc;
+
+import com.wordnik.swagger.core.ApiOperation;
+import com.wordnik.swagger.core.ApiParam;
+
+@Path(ApplicationComponentTemplateRestResource.URI_PATH)
+@Apidoc("Application Component Template resources")
+@Produces("application/json")
+public class ApplicationComponentTemplateRestResource extends AbstractCampRestResource {
+
+    public static final String URI_PATH = PlatformRestResource.CAMP_URI_PATH + "/application-component-templates";
+
+    @Path("/{id}")
+    @ApiOperation(value = "Get a specific application component template",
+        responseClass = ApplicationComponentTemplateDto.CLASS_NAME)
+    @GET
+    public ApplicationComponentTemplateDto get(
+            @ApiParam(value = "ID of item being retrieved", required = true)
+            @PathParam("id") String id) {
+        return dto().adapt(lookup(camp().applicationComponentTemplates(), id));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/AssemblyRestResource.java
----------------------------------------------------------------------
diff --git a/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/AssemblyRestResource.java b/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/AssemblyRestResource.java
new file mode 100644
index 0000000..c04c29b
--- /dev/null
+++ b/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/AssemblyRestResource.java
@@ -0,0 +1,34 @@
+package io.brooklyn.camp.rest.resource;
+
+import io.brooklyn.camp.dto.AssemblyDto;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+
+import brooklyn.rest.apidoc.Apidoc;
+
+import com.wordnik.swagger.core.ApiOperation;
+import com.wordnik.swagger.core.ApiParam;
+
+@Path(AssemblyRestResource.URI_PATH)
+@Apidoc("Assembly resources")
+@Produces("application/json")
+public class AssemblyRestResource extends AbstractCampRestResource {
+
+//    private static final Logger log = LoggerFactory.getLogger(AssemblyRestResource.class);
+    
+    public static final String URI_PATH = PlatformRestResource.CAMP_URI_PATH + "/assemblies";
+
+    @Path("/{id}")
+    @ApiOperation(value = "Get a specific assembly",
+            responseClass = AssemblyDto.CLASS_NAME)
+    @GET
+    public AssemblyDto get(
+            @ApiParam(value = "ID of item being retrieved", required = true)
+            @PathParam("id") String id) {
+        return dto().adapt(lookup(camp().assemblies(), id));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/AssemblyTemplateRestResource.java
----------------------------------------------------------------------
diff --git a/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/AssemblyTemplateRestResource.java b/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/AssemblyTemplateRestResource.java
new file mode 100644
index 0000000..ce78e0f
--- /dev/null
+++ b/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/AssemblyTemplateRestResource.java
@@ -0,0 +1,70 @@
+package io.brooklyn.camp.rest.resource;
+
+import io.brooklyn.camp.dto.AssemblyTemplateDto;
+import io.brooklyn.camp.spi.Assembly;
+import io.brooklyn.camp.spi.AssemblyTemplate;
+
+import java.net.URI;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.rest.apidoc.Apidoc;
+import brooklyn.util.exceptions.Exceptions;
+
+import com.wordnik.swagger.core.ApiOperation;
+import com.wordnik.swagger.core.ApiParam;
+
+@Path(AssemblyTemplateRestResource.URI_PATH)
+@Apidoc("Assembly Template resources")
+@Produces("application/json")
+public class AssemblyTemplateRestResource extends AbstractCampRestResource {
+
+    private static final Logger log = LoggerFactory.getLogger(AssemblyTemplateRestResource.class);
+    
+    public static final String URI_PATH = PlatformRestResource.CAMP_URI_PATH + "/assembly-templates";
+
+    @Path("/{id}")
+    @ApiOperation(value = "Get a specific assembly template",
+            responseClass = AssemblyTemplateDto.CLASS_NAME)
+    @GET
+    public AssemblyTemplateDto get(
+            @ApiParam(value = "ID of item being retrieved", required = true)
+            @PathParam("id") String id) {
+        return dto().adapt(lookup(camp().assemblyTemplates(), id));
+    }
+
+    @Path("/{id}")
+    @ApiOperation(value = "Instantiate a specific assembly template"
+    // TODO AssemblyDto, or location thereto?
+//            , responseClass = AssemblyTemplateDto.CLASS_NAME
+            )
+    @POST
+    public Response post(
+            @Context UriInfo info,
+            @ApiParam(value = "ID of item being retrieved", required = true)
+            @PathParam("id") String id) {
+        try {
+            log.info("CAMP REST instantiating AT "+id);
+            AssemblyTemplate at = lookup(camp().assemblyTemplates(), id);
+            Assembly assembly = at.getInstantiator().newInstance().instantiate(at, camp());
+            // see http://stackoverflow.com/questions/13702481/javax-response-prepends-method-path-when-setting-location-header-path-on-status
+            // for why we have to return absolute path
+            URI assemblyUri = info.getBaseUriBuilder().path( dto().adapt(assembly).getUri() ).build();
+            return Response.created(assemblyUri).build();
+        } catch (Exception e) {
+            log.error("Unable to create AT "+id+": "+e);
+            throw Exceptions.propagate(e);
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/PlatformComponentRestResource.java
----------------------------------------------------------------------
diff --git a/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/PlatformComponentRestResource.java b/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/PlatformComponentRestResource.java
new file mode 100644
index 0000000..e5b6f98
--- /dev/null
+++ b/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/PlatformComponentRestResource.java
@@ -0,0 +1,32 @@
+package io.brooklyn.camp.rest.resource;
+
+import io.brooklyn.camp.dto.PlatformComponentDto;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+
+import brooklyn.rest.apidoc.Apidoc;
+
+import com.wordnik.swagger.core.ApiOperation;
+import com.wordnik.swagger.core.ApiParam;
+
+@Path(PlatformComponentRestResource.URI_PATH)
+@Apidoc("Platform Component resources")
+@Produces("application/json")
+public class PlatformComponentRestResource extends AbstractCampRestResource {
+
+    public static final String URI_PATH = PlatformRestResource.CAMP_URI_PATH + "/platform-components";
+
+    @Path("/{id}")
+    @ApiOperation(value = "Get a specific platform component",
+            responseClass = PlatformComponentDto.CLASS_NAME)
+    @GET
+    public PlatformComponentDto get(
+            @ApiParam(value = "ID of item being retrieved", required = true)
+            @PathParam("id") String id) {
+        return dto().adapt(lookup(camp().platformComponents(), id));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/PlatformComponentTemplateRestResource.java
----------------------------------------------------------------------
diff --git a/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/PlatformComponentTemplateRestResource.java b/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/PlatformComponentTemplateRestResource.java
new file mode 100644
index 0000000..0baa4ec
--- /dev/null
+++ b/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/PlatformComponentTemplateRestResource.java
@@ -0,0 +1,32 @@
+package io.brooklyn.camp.rest.resource;
+
+import io.brooklyn.camp.dto.PlatformComponentTemplateDto;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+
+import brooklyn.rest.apidoc.Apidoc;
+
+import com.wordnik.swagger.core.ApiOperation;
+import com.wordnik.swagger.core.ApiParam;
+
+@Path(PlatformComponentTemplateRestResource.URI_PATH)
+@Apidoc("Platform Component Template resources")
+@Produces("application/json")
+public class PlatformComponentTemplateRestResource extends AbstractCampRestResource {
+
+    public static final String URI_PATH = PlatformRestResource.CAMP_URI_PATH + "/platform-component-templates";
+
+    @Path("/{id}")
+    @ApiOperation(value = "Get a specific platform component template",
+            responseClass = PlatformComponentTemplateDto.CLASS_NAME)
+    @GET
+    public PlatformComponentTemplateDto get(
+            @ApiParam(value = "ID of item being retrieved", required = true)
+            @PathParam("id") String id) {
+        return dto().adapt(lookup(camp().platformComponentTemplates(), id));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/PlatformRestResource.java
----------------------------------------------------------------------
diff --git a/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/PlatformRestResource.java b/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/PlatformRestResource.java
new file mode 100644
index 0000000..cf647f6
--- /dev/null
+++ b/camp/camp-server/src/main/java/io/brooklyn/camp/rest/resource/PlatformRestResource.java
@@ -0,0 +1,71 @@
+package io.brooklyn.camp.rest.resource;
+
+import io.brooklyn.camp.dto.PlatformDto;
+import io.brooklyn.camp.rest.util.WebResourceUtils;
+import io.brooklyn.camp.spi.AssemblyTemplate;
+
+import java.io.InputStream;
+import java.io.StringReader;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.rest.apidoc.Apidoc;
+
+import com.wordnik.swagger.core.ApiOperation;
+
+//import io.brooklyn.camp.rest.apidoc.Apidoc;
+
+@Path(PlatformRestResource.CAMP_URI_PATH)
+@Apidoc("Platform (root)")
+@Produces("application/json")
+public class PlatformRestResource extends AbstractCampRestResource {
+
+    private static final Logger log = LoggerFactory.getLogger(PlatformRestResource.class);
+    
+    public static final String CAMP_URI_PATH = "/camp/v11";
+    
+    @ApiOperation(value = "Return the Platform (root) resource",
+            responseClass = PlatformDto.CLASS_NAME)
+    @GET
+    public PlatformDto get() {
+        return dto().adapt(camp().root());
+    }
+    
+    @POST
+    @Consumes({MediaType.APPLICATION_JSON})
+    public Response postJson(@Context UriInfo info, String json) {
+        return postYaml(info, json);
+    }
+
+    @POST
+    @Consumes({"application/x-yaml"})
+    public Response postYaml(@Context UriInfo info, String yaml) {
+        log.debug("YAML pdp:\n"+yaml);
+        AssemblyTemplate template = camp().pdp().registerDeploymentPlan(new StringReader(yaml));
+        return created(info, template);
+    }
+
+    @POST
+    @Consumes({"application/x-tar", "application/x-tgz", "application/x-zip"})
+    public Response postArchive(@Context UriInfo info, InputStream archiveInput) {
+        log.debug("ARCHIVE pdp");
+        AssemblyTemplate template = camp().pdp().registerPdpFromArchive(archiveInput);
+        return created(info, template);
+    }
+
+    protected Response created(UriInfo info, AssemblyTemplate template) {
+        return WebResourceUtils.created(info, dto().adapt(template).getUri());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-server/src/main/java/io/brooklyn/camp/rest/util/CampJsons.java
----------------------------------------------------------------------
diff --git a/camp/camp-server/src/main/java/io/brooklyn/camp/rest/util/CampJsons.java b/camp/camp-server/src/main/java/io/brooklyn/camp/rest/util/CampJsons.java
new file mode 100644
index 0000000..094ab4d
--- /dev/null
+++ b/camp/camp-server/src/main/java/io/brooklyn/camp/rest/util/CampJsons.java
@@ -0,0 +1,21 @@
+package io.brooklyn.camp.rest.util;
+
+import brooklyn.util.exceptions.Exceptions;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+
+public class CampJsons {
+
+    public static String prettyJson(Object o) {
+        try {
+            ObjectMapper mapper = new ObjectMapper();
+            mapper.enable(SerializationFeature.INDENT_OUTPUT);
+            return mapper.writeValueAsString(o);
+        } catch (JsonProcessingException e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-server/src/main/java/io/brooklyn/camp/rest/util/CampRestContext.java
----------------------------------------------------------------------
diff --git a/camp/camp-server/src/main/java/io/brooklyn/camp/rest/util/CampRestContext.java b/camp/camp-server/src/main/java/io/brooklyn/camp/rest/util/CampRestContext.java
new file mode 100644
index 0000000..c533bd2
--- /dev/null
+++ b/camp/camp-server/src/main/java/io/brooklyn/camp/rest/util/CampRestContext.java
@@ -0,0 +1,32 @@
+package io.brooklyn.camp.rest.util;
+
+import javax.servlet.ServletContext;
+
+import com.google.common.base.Preconditions;
+
+import io.brooklyn.camp.CampPlatform;
+import io.brooklyn.camp.CampServer;
+
+public class CampRestContext {
+
+    private final ServletContext servletContext;
+    private CampPlatform platform;
+    private DtoFactory dto;
+    
+    public CampRestContext(ServletContext servletContext) {
+        this.servletContext = servletContext;
+    }
+
+    public synchronized CampPlatform camp() {
+        if (platform!=null) return platform;
+        platform = (CampPlatform) servletContext.getAttribute(CampServer.CAMP_PLATFORM_ATTRIBUTE);
+        return Preconditions.checkNotNull(platform, "CAMP platform instance not available from ServletContext");
+    }
+
+    public DtoFactory dto() {
+        if (dto!=null) return dto;
+        dto = (DtoFactory) servletContext.getAttribute(CampServer.DTO_FACTORY);
+        return Preconditions.checkNotNull(dto, "CAMP DTO factory instance not available from ServletContext");
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-server/src/main/java/io/brooklyn/camp/rest/util/CampRestGuavas.java
----------------------------------------------------------------------
diff --git a/camp/camp-server/src/main/java/io/brooklyn/camp/rest/util/CampRestGuavas.java b/camp/camp-server/src/main/java/io/brooklyn/camp/rest/util/CampRestGuavas.java
new file mode 100644
index 0000000..ffded5e
--- /dev/null
+++ b/camp/camp-server/src/main/java/io/brooklyn/camp/rest/util/CampRestGuavas.java
@@ -0,0 +1,14 @@
+package io.brooklyn.camp.rest.util;
+
+import io.brooklyn.camp.spi.AbstractResource;
+
+import com.google.common.base.Function;
+
+public class CampRestGuavas {
+
+    public static final Function<AbstractResource,String> IDENTITY_OF_REST_RESOURCE = 
+            new Function<AbstractResource,String>() {
+                public String apply(AbstractResource input) { return input.getId(); }
+            };
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-server/src/main/java/io/brooklyn/camp/rest/util/DtoFactory.java
----------------------------------------------------------------------
diff --git a/camp/camp-server/src/main/java/io/brooklyn/camp/rest/util/DtoFactory.java b/camp/camp-server/src/main/java/io/brooklyn/camp/rest/util/DtoFactory.java
new file mode 100644
index 0000000..8d3e593
--- /dev/null
+++ b/camp/camp-server/src/main/java/io/brooklyn/camp/rest/util/DtoFactory.java
@@ -0,0 +1,158 @@
+package io.brooklyn.camp.rest.util;
+
+import io.brooklyn.camp.CampPlatform;
+import io.brooklyn.camp.dto.ApplicationComponentDto;
+import io.brooklyn.camp.dto.ApplicationComponentTemplateDto;
+import io.brooklyn.camp.dto.AssemblyDto;
+import io.brooklyn.camp.dto.AssemblyTemplateDto;
+import io.brooklyn.camp.dto.PlatformComponentDto;
+import io.brooklyn.camp.dto.PlatformComponentTemplateDto;
+import io.brooklyn.camp.dto.PlatformDto;
+import io.brooklyn.camp.rest.resource.AbstractCampRestResource;
+import io.brooklyn.camp.rest.resource.ApplicationComponentRestResource;
+import io.brooklyn.camp.rest.resource.ApplicationComponentTemplateRestResource;
+import io.brooklyn.camp.rest.resource.AssemblyRestResource;
+import io.brooklyn.camp.rest.resource.AssemblyTemplateRestResource;
+import io.brooklyn.camp.rest.resource.PlatformComponentRestResource;
+import io.brooklyn.camp.rest.resource.PlatformComponentTemplateRestResource;
+import io.brooklyn.camp.rest.resource.PlatformRestResource;
+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 java.util.Map;
+
+import javax.ws.rs.Path;
+
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.net.Urls;
+
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+
+public class DtoFactory {
+
+    private CampPlatform platform;
+    private String uriBase;
+    
+    private UriFactory uriFactory;
+
+    public DtoFactory(CampPlatform campPlatform, String uriBase) {
+        this.platform = campPlatform;
+        this.uriBase = uriBase;
+        
+        uriFactory = new UriFactory();
+        uriFactory.registerIdentifiableRestResource(PlatformRootSummary.class, PlatformRestResource.class);
+        uriFactory.registerIdentifiableRestResource(AssemblyTemplate.class, AssemblyTemplateRestResource.class);
+        uriFactory.registerIdentifiableRestResource(PlatformComponentTemplate.class, PlatformComponentTemplateRestResource.class);
+        uriFactory.registerIdentifiableRestResource(ApplicationComponentTemplate.class, ApplicationComponentTemplateRestResource.class);
+        uriFactory.registerIdentifiableRestResource(Assembly.class, AssemblyRestResource.class);
+        uriFactory.registerIdentifiableRestResource(PlatformComponent.class, PlatformComponentRestResource.class);
+        uriFactory.registerIdentifiableRestResource(ApplicationComponent.class, ApplicationComponentRestResource.class);
+    }
+
+    public CampPlatform getPlatform() {
+        return platform;
+    }
+
+    public UriFactory getUriFactory() {
+        return uriFactory;
+    }
+
+    public String uri(AbstractResource x) {
+        return getUriFactory().uri(x);
+    }
+        
+    public String uri(Class<? extends AbstractResource> targetType, String id) {
+        return getUriFactory().uri(targetType, id);
+    }
+
+    public AssemblyTemplateDto adapt(AssemblyTemplate assemblyTemplate) {
+        return AssemblyTemplateDto.newInstance(this, assemblyTemplate);
+    }
+    public PlatformComponentTemplateDto adapt(PlatformComponentTemplate platformComponentTemplate) {
+        return PlatformComponentTemplateDto.newInstance(this, platformComponentTemplate);
+    }
+    public ApplicationComponentTemplateDto adapt(ApplicationComponentTemplate applicationComponentTemplate) {
+        return ApplicationComponentTemplateDto.newInstance(this, applicationComponentTemplate);
+    }
+
+    public AssemblyDto adapt(Assembly assembly) {
+        return AssemblyDto.newInstance(this, assembly);
+    }
+    public PlatformComponentDto adapt(PlatformComponent platformComponent) {
+        return PlatformComponentDto.newInstance(this, platformComponent);
+    }
+    public ApplicationComponentDto adapt(ApplicationComponent applicationComponent) {
+        return ApplicationComponentDto.newInstance(this, applicationComponent);
+    }
+
+    public PlatformDto adapt(PlatformRootSummary root) {
+        return PlatformDto.newInstance(this, root);
+    }
+
+    public class UriFactory {
+        /** registry of generating a URI given an object */
+        Map<Class<?>,Function<Object,String>> registryResource = new MutableMap<Class<?>, Function<Object,String>>();
+        /** registry of generating a URI given an ID */
+        Map<Class<?>,Function<String,String>> registryId = new MutableMap<Class<?>, Function<String,String>>();
+
+        /** registers a function which generates a URI given a type; note that this method cannot be used for links */
+        @SuppressWarnings("unchecked")
+        public synchronized <T> void registerResourceUriFunction(Class<T> type, Function<T,String> fnUri) {
+            registryResource.put(type, (Function<Object, String>) fnUri);
+        }
+
+        /** registers a type to generate a URI which concatenates the given base with the
+         * result of the given function to generate an ID against an object of the given type */
+        public synchronized <T> void registerIdentityFunction(Class<T> type, final String resourceTypeUriBase, final Function<T,String> fnIdentity) {
+            final Function<String,String> fnUriFromId = new Function<String,String>() {
+                public String apply(String id) {
+                    return Urls.mergePaths(resourceTypeUriBase, id);
+                }
+            };
+            registryId.put(type, (Function<String, String>) fnUriFromId);
+            registerResourceUriFunction(type, new Function<T,String>() {
+                public String apply(T input) {
+                    return fnUriFromId.apply(fnIdentity.apply(input));
+                }
+            });
+        }
+
+        /** registers a CAMP Resource type against a RestResource, generating the URI
+         * by concatenating the @Path annotation on the RestResource with the ID of the CAMP resource */
+        @SuppressWarnings({ "unchecked", "rawtypes" })
+        public synchronized <T extends AbstractResource> void registerIdentifiableRestResource(Class<T> type, Class<? extends AbstractCampRestResource> restResource) {
+            registerIdentityFunction(type, 
+                    uriOfRestResource(restResource),
+                    (Function) CampRestGuavas.IDENTITY_OF_REST_RESOURCE);
+        }
+        
+        public String uri(Class<? extends AbstractResource> targetType, String id) {
+            return Preconditions.checkNotNull(registryId.get(targetType), 
+                    "No REST ID converter registered for %s (id %s)", targetType, id)
+                    .apply(id);
+        }
+
+        public String uri(AbstractResource x) {
+            return Preconditions.checkNotNull(registryResource.get(x.getClass()), 
+                    "No REST converter registered for %s (%s)", x.getClass(), x)
+                    .apply(x);
+        }
+        
+        public String uriOfRestResource(Class<?> restResourceClass) {
+            return Urls.mergePaths(uriBase, 
+                    Preconditions.checkNotNull(restResourceClass.getAnnotation(Path.class),
+                            "No @Path on type %s", restResourceClass)
+                    .value());
+        }
+            
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-server/src/main/java/io/brooklyn/camp/rest/util/WebResourceUtils.java
----------------------------------------------------------------------
diff --git a/camp/camp-server/src/main/java/io/brooklyn/camp/rest/util/WebResourceUtils.java b/camp/camp-server/src/main/java/io/brooklyn/camp/rest/util/WebResourceUtils.java
new file mode 100644
index 0000000..3e162e0
--- /dev/null
+++ b/camp/camp-server/src/main/java/io/brooklyn/camp/rest/util/WebResourceUtils.java
@@ -0,0 +1,42 @@
+package io.brooklyn.camp.rest.util;
+
+import io.brooklyn.camp.dto.ApiErrorDto;
+
+import java.net.URI;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class WebResourceUtils {
+
+    private static final Logger log = LoggerFactory.getLogger(WebResourceUtils.class);
+    
+    public static WebApplicationException notFound(String format, Object... args) {
+        String msg = String.format(format, args);
+        if (log.isDebugEnabled()) log.debug("returning 404 notFound("+msg+")");
+        throw new WebApplicationException(Response.status(Response.Status.NOT_FOUND)
+                .type(MediaType.APPLICATION_JSON_TYPE)
+                .entity(ApiErrorDto.builder().message(msg).build()).build());
+    }
+
+    public static WebApplicationException preconditionFailed(String format, Object... args) {
+        String msg = String.format(format, args);
+        if (log.isDebugEnabled()) log.debug("returning 412 preconditionFailed("+msg+")");
+        throw new WebApplicationException(Response.status(Response.Status.PRECONDITION_FAILED)
+                .type(MediaType.APPLICATION_JSON_TYPE)
+                .entity(ApiErrorDto.builder().message(msg).build()).build());
+    }
+
+    public static Response created(UriInfo info, String resourceUriPath) {
+        // see http://stackoverflow.com/questions/13702481/javax-response-prepends-method-path-when-setting-location-header-path-on-status
+        // for why we have to return absolute path
+        URI resourceUri = info.getBaseUriBuilder().path( resourceUriPath ).build();
+        return Response.created(resourceUri).build();
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-server/src/test/java/io/brooklyn/camp/dto/ApplicationCompomentTemplateDtoTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-server/src/test/java/io/brooklyn/camp/dto/ApplicationCompomentTemplateDtoTest.java b/camp/camp-server/src/test/java/io/brooklyn/camp/dto/ApplicationCompomentTemplateDtoTest.java
new file mode 100644
index 0000000..36686e2
--- /dev/null
+++ b/camp/camp-server/src/test/java/io/brooklyn/camp/dto/ApplicationCompomentTemplateDtoTest.java
@@ -0,0 +1,31 @@
+package io.brooklyn.camp.dto;
+
+import io.brooklyn.camp.CampPlatform;
+import io.brooklyn.camp.rest.util.DtoFactory;
+import io.brooklyn.camp.spi.ApplicationComponentTemplate;
+import io.brooklyn.camp.test.mock.web.MockWebPlatform;
+import junit.framework.Assert;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+
+public class ApplicationCompomentTemplateDtoTest {
+
+    private static final Logger log = LoggerFactory.getLogger(ApplicationCompomentTemplateDtoTest.class);
+    
+    @Test
+    public void testAppServerPct() {
+        CampPlatform p = MockWebPlatform.newPlatform();
+        DtoFactory f = new DtoFactory(p, "");
+        
+        ApplicationComponentTemplate t = MockWebPlatform.WAR;
+        ApplicationComponentTemplateDto dto = f.adapt(t);
+        
+        log.info("War PCT serialized as: "+BasicDtoTest.tree(dto));
+        Assert.assertEquals(dto.getName(), t.getName());
+        Assert.assertNotNull(dto.getCreatedAsString());
+        Assert.assertTrue(dto.getCreatedAsString().startsWith("20"));
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-server/src/test/java/io/brooklyn/camp/dto/BasicDtoTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-server/src/test/java/io/brooklyn/camp/dto/BasicDtoTest.java b/camp/camp-server/src/test/java/io/brooklyn/camp/dto/BasicDtoTest.java
new file mode 100644
index 0000000..a21c3fd
--- /dev/null
+++ b/camp/camp-server/src/test/java/io/brooklyn/camp/dto/BasicDtoTest.java
@@ -0,0 +1,72 @@
+package io.brooklyn.camp.dto;
+
+import java.io.IOException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.exceptions.Exceptions;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/** Tests identity methods and custom attributes for DTO, including Jackson JSON serialization */
+public class BasicDtoTest {
+
+    private static final Logger log = LoggerFactory.getLogger(BasicDtoTest.class);
+    
+    @Test
+    public void testSimple() throws IOException {
+        DtoCustomAttributes l = new DtoCustomAttributes(null);
+        
+        JsonNode t = tree(l);
+        Assert.assertEquals(t.size(), 0);
+        Assert.assertTrue(l.getCustomAttributes()==null || l.getCustomAttributes().isEmpty());
+        
+        Assert.assertEquals(l, new ObjectMapper().readValue(t.toString(), DtoCustomAttributes.class));
+    }
+
+    @Test
+    public void testCustomAttrs() throws IOException {
+        DtoCustomAttributes l = new DtoCustomAttributes(MutableMap.of("bar", "bee"));
+        
+        JsonNode t = tree(l);
+        Assert.assertEquals(t.size(), 1);
+        Assert.assertEquals(t.get("bar").asText(), l.getCustomAttributes().get("bar"));
+        
+        Assert.assertEquals(l, new ObjectMapper().readValue(t.toString(), DtoCustomAttributes.class));
+    }
+
+    @Test
+    public void testIdentity() throws IOException {
+        DtoCustomAttributes l1 = new DtoCustomAttributes(null);
+        DtoCustomAttributes l2 = new DtoCustomAttributes(MutableMap.of("bar", "bee"));
+        DtoCustomAttributes l2o = new DtoCustomAttributes(MutableMap.of("bar", "bee"));
+        
+        Assert.assertEquals(l1, l1);
+        Assert.assertEquals(l2, l2);
+        Assert.assertEquals(l2, l2o);
+        Assert.assertNotEquals(l1, l2);
+        
+        Assert.assertEquals(l1.hashCode(), l1.hashCode());
+        Assert.assertEquals(l2.hashCode(), l2.hashCode());
+        Assert.assertEquals(l2.hashCode(), l2o.hashCode());
+        Assert.assertNotEquals(l1.hashCode(), l2.hashCode());
+    }
+    
+    public static JsonNode tree(Object l) {
+        try {
+            ObjectMapper m = new ObjectMapper();
+            String s = m.writeValueAsString(l);
+            log.info(l.toString()+" -> "+s);
+            JsonNode t = m.readTree(s);
+            return t;
+        } catch (Exception e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-server/src/test/java/io/brooklyn/camp/dto/LinkDtoTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-server/src/test/java/io/brooklyn/camp/dto/LinkDtoTest.java b/camp/camp-server/src/test/java/io/brooklyn/camp/dto/LinkDtoTest.java
new file mode 100644
index 0000000..da2c344
--- /dev/null
+++ b/camp/camp-server/src/test/java/io/brooklyn/camp/dto/LinkDtoTest.java
@@ -0,0 +1,44 @@
+package io.brooklyn.camp.dto;
+
+import java.io.IOException;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import brooklyn.util.collections.MutableMap;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+@Test
+public class LinkDtoTest {
+
+//    private static final Logger log = LoggerFactory.getLogger(LinkDtoTest.class);
+    
+    @Test
+    public void testSimple() throws IOException {
+        LinkDto l = LinkDto.newInstance("http://foo", "Foo");
+        
+        JsonNode t = BasicDtoTest.tree(l);
+        Assert.assertEquals(t.size(), 2);
+        Assert.assertEquals(t.get("href").asText(), l.getHref());
+        Assert.assertEquals(t.get("targetName").asText(), l.getTargetName());
+        Assert.assertTrue(l.getCustomAttributes()==null || l.getCustomAttributes().isEmpty());
+        
+        Assert.assertEquals(l, new ObjectMapper().readValue(t.toString(), LinkDto.class));
+    }
+
+    @Test
+    public void testCustomAttrs() throws IOException {
+        LinkDto l = LinkDto.newInstance("http://foo", "Foo", MutableMap.of("bar", "bee"));
+        
+        JsonNode t = BasicDtoTest.tree(l);
+        Assert.assertEquals(t.size(), 3);
+        Assert.assertEquals(t.get("href").asText(), l.getHref());
+        Assert.assertEquals(t.get("targetName").asText(), l.getTargetName());
+        Assert.assertEquals(t.get("bar").asText(), l.getCustomAttributes().get("bar"));
+        
+        Assert.assertEquals(l, new ObjectMapper().readValue(t.toString(), LinkDto.class));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-server/src/test/java/io/brooklyn/camp/dto/PlatformCompomentTemplateDtoTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-server/src/test/java/io/brooklyn/camp/dto/PlatformCompomentTemplateDtoTest.java b/camp/camp-server/src/test/java/io/brooklyn/camp/dto/PlatformCompomentTemplateDtoTest.java
new file mode 100644
index 0000000..25b9f29
--- /dev/null
+++ b/camp/camp-server/src/test/java/io/brooklyn/camp/dto/PlatformCompomentTemplateDtoTest.java
@@ -0,0 +1,31 @@
+package io.brooklyn.camp.dto;
+
+import io.brooklyn.camp.CampPlatform;
+import io.brooklyn.camp.rest.util.DtoFactory;
+import io.brooklyn.camp.spi.PlatformComponentTemplate;
+import io.brooklyn.camp.test.mock.web.MockWebPlatform;
+import junit.framework.Assert;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+
+public class PlatformCompomentTemplateDtoTest {
+
+    private static final Logger log = LoggerFactory.getLogger(PlatformCompomentTemplateDtoTest.class);
+    
+    @Test
+    public void testAppServerPct() {
+        CampPlatform p = MockWebPlatform.newPlatform();
+        DtoFactory f = new DtoFactory(p, "");
+        
+        PlatformComponentTemplate t = MockWebPlatform.APPSERVER;
+        PlatformComponentTemplateDto dto = f.adapt(t);
+        
+        log.info("Web PCT serialized as: "+BasicDtoTest.tree(dto));
+        Assert.assertEquals(dto.getName(), t.getName());
+        Assert.assertNotNull(dto.getCreatedAsString());
+        Assert.assertTrue(dto.getCreatedAsString().startsWith("20"));
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-server/src/test/java/io/brooklyn/camp/dto/ResourceDtoTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-server/src/test/java/io/brooklyn/camp/dto/ResourceDtoTest.java b/camp/camp-server/src/test/java/io/brooklyn/camp/dto/ResourceDtoTest.java
new file mode 100644
index 0000000..ed8c4cd
--- /dev/null
+++ b/camp/camp-server/src/test/java/io/brooklyn/camp/dto/ResourceDtoTest.java
@@ -0,0 +1,59 @@
+package io.brooklyn.camp.dto;
+
+import io.brooklyn.camp.BasicCampPlatform;
+import io.brooklyn.camp.CampServer;
+import io.brooklyn.camp.commontypes.RepresentationSkew;
+import io.brooklyn.camp.rest.util.CampRestGuavas;
+import io.brooklyn.camp.spi.AbstractResource;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+@Test
+public class ResourceDtoTest {
+
+//    private static final Logger log = LoggerFactory.getLogger(ResourceDtoTest.class);
+    
+    CampServer s;
+    AbstractResource rr;
+    ResourceDto r;
+    
+    @SuppressWarnings("unchecked")
+    protected void initSimpleDto() {
+        s = new CampServer(new BasicCampPlatform(), "http://atest/");
+        s.getDtoFactory().getUriFactory().registerIdentityFunction(AbstractResource.class, "basic", CampRestGuavas.IDENTITY_OF_REST_RESOURCE);
+        rr = AbstractResource.builder().name("Name").description("a description").
+                tags(Arrays.asList("tag1", "tag 2")).representationSkew(RepresentationSkew.NONE).build();
+        r = ResourceDto.newInstance(s.getDtoFactory(), rr);
+    }
+    
+    @Test
+    public void testSimpleCreation() throws IOException {
+        initSimpleDto();
+        
+        Assert.assertNotNull(r.getCreatedAsString());
+        Assert.assertEquals(r.getName(), "Name");
+        Assert.assertEquals(r.getDescription(), "a description");
+        Assert.assertEquals(r.getTags(), Arrays.asList("tag1", "tag 2"));
+        Assert.assertEquals(r.getRepresentationSkew(), RepresentationSkew.NONE);
+    }
+    
+    public void testSimpleSerializationAndDeserialization() throws IOException {
+        initSimpleDto();
+        
+        JsonNode t = BasicDtoTest.tree(r);
+        
+//        Assert.assertEquals(t.get("uri").asText(), r.getUri());
+        ResourceDto r2 = new ObjectMapper().readValue(t.toString(), ResourceDto.class);
+        Assert.assertNotNull(r2.getCreated());
+        Assert.assertEquals(r, r2);
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-server/src/test/java/io/brooklyn/camp/rest/resource/PlatformRestResourceTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-server/src/test/java/io/brooklyn/camp/rest/resource/PlatformRestResourceTest.java b/camp/camp-server/src/test/java/io/brooklyn/camp/rest/resource/PlatformRestResourceTest.java
new file mode 100644
index 0000000..1db19ad
--- /dev/null
+++ b/camp/camp-server/src/test/java/io/brooklyn/camp/rest/resource/PlatformRestResourceTest.java
@@ -0,0 +1,24 @@
+package io.brooklyn.camp.rest.resource;
+
+import io.brooklyn.camp.dto.PlatformComponentTemplateDto;
+import io.brooklyn.camp.dto.PlatformDto;
+import io.brooklyn.camp.test.fixture.AbstractRestResourceTest;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class PlatformRestResourceTest extends AbstractRestResourceTest {
+
+    private static final Logger log = LoggerFactory.getLogger(PlatformRestResourceTest.class);
+    
+    @Test
+    public void testPlatformIncludesList() {
+        PlatformDto p = load(PlatformRestResource.CAMP_URI_PATH, PlatformDto.class);
+        PlatformComponentTemplateDto pct = load(p.getPlatformComponentTemplates().get(0).getHref(), PlatformComponentTemplateDto.class);
+        log.debug("Loaded PCT via REST: "+pct);
+        Assert.assertNotNull(pct.getName());
+    }
+        
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-server/src/test/java/io/brooklyn/camp/test/fixture/AbstractRestResourceTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-server/src/test/java/io/brooklyn/camp/test/fixture/AbstractRestResourceTest.java b/camp/camp-server/src/test/java/io/brooklyn/camp/test/fixture/AbstractRestResourceTest.java
new file mode 100644
index 0000000..d4c6f01
--- /dev/null
+++ b/camp/camp-server/src/test/java/io/brooklyn/camp/test/fixture/AbstractRestResourceTest.java
@@ -0,0 +1,68 @@
+package io.brooklyn.camp.test.fixture;
+
+import java.net.URL;
+
+import io.brooklyn.camp.BasicCampPlatform;
+import io.brooklyn.camp.CampServer;
+import io.brooklyn.camp.test.mock.web.MockWebPlatform;
+
+import org.junit.AfterClass;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.BeforeClass;
+import org.testng.reporters.Files;
+
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.net.Urls;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class AbstractRestResourceTest {
+
+    private static final Logger log = LoggerFactory.getLogger(AbstractRestResourceTest.class);
+    
+    protected BasicCampPlatform platform;
+    protected CampServer server;
+    
+    @BeforeClass
+    public void startServer() {
+        platform = new BasicCampPlatform();
+        populate();
+        
+        // new server
+        server = new CampServer(platform, "").start();
+    }
+    
+    protected void populate() {
+        MockWebPlatform.populate(platform);
+    }
+
+    @AfterClass 
+    public void stopServer() {
+        if (server!=null)
+            server.stop();
+    }
+    
+    public String load(String path) {
+        try {
+            String base = "http://localhost:"+server.getPort();
+            String x = path.startsWith(base) ? path : Urls.mergePaths(base, path);
+            log.debug("Reading from: "+x);
+            String s = Files.streamToString(new URL(x).openStream());
+            log.debug("Result from "+x+": "+s);
+            return s;
+        } catch (Exception e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+    
+    public <T> T load(String path, Class<T> type) {
+        try {
+            String data = load(path);
+            return new ObjectMapper().readValue(data, type);
+        } catch (Exception e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d2191c9a/camp/camp-server/src/test/java/io/brooklyn/camp/test/fixture/InMemoryCamp.java
----------------------------------------------------------------------
diff --git a/camp/camp-server/src/test/java/io/brooklyn/camp/test/fixture/InMemoryCamp.java b/camp/camp-server/src/test/java/io/brooklyn/camp/test/fixture/InMemoryCamp.java
new file mode 100644
index 0000000..a873117
--- /dev/null
+++ b/camp/camp-server/src/test/java/io/brooklyn/camp/test/fixture/InMemoryCamp.java
@@ -0,0 +1,35 @@
+package io.brooklyn.camp.test.fixture;
+
+import io.brooklyn.camp.BasicCampPlatform;
+import io.brooklyn.camp.CampServer;
+import io.brooklyn.camp.test.mock.web.MockWebPlatform;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class InMemoryCamp {
+    
+    private static final Logger log = LoggerFactory.getLogger(InMemoryCamp.class);
+
+    
+    public static void main(String[] args) {
+        
+        // new platform with some mock types and some data structures
+        
+            // interface CampComponent
+            // getComponentTemplate() -> operations, links, etc
+        
+            // platformView.getComponent(id) -> returns instance of domain-specific component type
+        BasicCampPlatform p = new BasicCampPlatform();
+        MockWebPlatform.populate(p);
+        
+        // new server
+        CampServer s = new CampServer(p, "").start();
+        
+        log.info("Running at: "+s.getUriBase());
+        // requests against server
+        
+    }
+    
+    
+}