You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@polygene.apache.org by ni...@apache.org on 2015/08/02 12:39:54 UTC

[2/4] zest-java git commit: ZEST-111; First commit to the Restful Library.

http://git-wip-us.apache.org/repos/asf/zest-java/blob/9304d008/libraries/restlet/src/main/java/org/apache/zest/library/restlet/assembly/resource/ResourceLayer.java
----------------------------------------------------------------------
diff --git a/libraries/restlet/src/main/java/org/apache/zest/library/restlet/assembly/resource/ResourceLayer.java b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/assembly/resource/ResourceLayer.java
new file mode 100644
index 0000000..fcb814d
--- /dev/null
+++ b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/assembly/resource/ResourceLayer.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.zest.library.restlet.assembly.resource;
+
+import org.apache.zest.bootstrap.AssemblyException;
+import org.apache.zest.bootstrap.LayerAssembly;
+import org.apache.zest.bootstrap.layered.LayerAssembler;
+import org.apache.zest.bootstrap.layered.LayeredLayerAssembler;
+
+public class ResourceLayer extends LayeredLayerAssembler
+    implements LayerAssembler
+{
+    public static final String NAME = "Resource Layer";
+
+    @Override
+    public LayerAssembly assemble( LayerAssembly layer )
+        throws AssemblyException
+    {
+        return layer;
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/9304d008/libraries/restlet/src/main/java/org/apache/zest/library/restlet/crud/EntityList.java
----------------------------------------------------------------------
diff --git a/libraries/restlet/src/main/java/org/apache/zest/library/restlet/crud/EntityList.java b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/crud/EntityList.java
new file mode 100644
index 0000000..3bae7fc
--- /dev/null
+++ b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/crud/EntityList.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.zest.library.restlet.crud;
+
+import java.util.List;
+import org.apache.zest.api.entity.Identity;
+import org.apache.zest.api.property.Property;
+import org.apache.zest.library.restlet.Command;
+
+public interface EntityList extends Identity
+{
+    Property<List<EntityRef>> entities();
+
+    Property<List<Command>> commands();
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/9304d008/libraries/restlet/src/main/java/org/apache/zest/library/restlet/crud/EntityListResource.java
----------------------------------------------------------------------
diff --git a/libraries/restlet/src/main/java/org/apache/zest/library/restlet/crud/EntityListResource.java b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/crud/EntityListResource.java
new file mode 100644
index 0000000..63a5d4b
--- /dev/null
+++ b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/crud/EntityListResource.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.zest.library.restlet.crud;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.StreamSupport;
+import org.apache.zest.api.entity.Identity;
+import org.apache.zest.api.injection.scope.Service;
+import org.apache.zest.api.injection.scope.Structure;
+import org.apache.zest.api.injection.scope.This;
+import org.apache.zest.api.mixin.Mixins;
+import org.apache.zest.api.property.Property;
+import org.apache.zest.api.value.ValueBuilder;
+import org.apache.zest.api.value.ValueBuilderFactory;
+import org.apache.zest.library.restlet.FormField;
+import org.apache.zest.library.restlet.RestForm;
+import org.apache.zest.library.restlet.RestLink;
+import org.apache.zest.library.restlet.identity.IdentityManager;
+import org.apache.zest.library.restlet.repository.CrudRepository;
+import org.apache.zest.library.restlet.repository.RepositoryLocator;
+import org.apache.zest.library.restlet.resource.ResourceBuilder;
+import org.apache.zest.library.restlet.resource.ServerResource;
+import org.restlet.Request;
+import org.restlet.data.Method;
+import org.restlet.data.Reference;
+
+@Mixins( EntityListResource.Mixin.class )
+public interface EntityListResource<T extends Identity> extends ServerResource<EntityList>
+{
+    abstract class Mixin<T extends Identity>
+        implements EntityListResource<T>
+    {
+        @This
+        private Parameters<T> parameters;
+
+        @Structure
+        private ValueBuilderFactory vbf;
+
+        @Service
+        private ResourceBuilder resourceBuilder;
+
+        @Service
+        private RepositoryLocator locator;
+
+        @Service
+        private IdentityManager identityManager;
+
+        @Override
+        public EntityList get()
+        {
+            Property<Request> request = parameters.request();
+            Reference base = request.get().getResourceRef();
+            String identity = identityManager.generate( EntityListResource.class, "list[" + parameters.entityType().get().getSimpleName() +"]" );
+            ValueBuilder<EntityList> builder = vbf.newValueBuilder( EntityList.class );
+            List<EntityRef> result = getEntityRefs( base );
+            EntityList prototype = builder.prototype();
+            prototype.identity().set( identity );
+            prototype.entities().set( Collections.unmodifiableList( result ) );
+            prototype.commands().set( Collections.singletonList( resourceBuilder.createCommand( base ) ) );
+            return builder.newInstance();
+        }
+
+        @Override
+        public RestLink post( RestForm form )
+        {
+            FormField nameField = form.field( "name" );
+            String name = null;
+            if( nameField != null )
+            {
+                name = nameField.value().get();
+            }
+            Reference base = parameters.request().get().getResourceRef();
+
+            //noinspection unchecked
+            Class<T> entityType = parameters.entityType().get();
+
+            identityManager.generate( entityType, name );
+            locator.find( entityType ).create( name );
+            return resourceBuilder.createRestLink( name, base, Method.GET );
+        }
+
+        @SuppressWarnings( "unchecked" )
+        private List<EntityRef> getEntityRefs( Reference base )
+        {
+            ArrayList result = new ArrayList<>();
+            Class<T> entityType = parameters.entityType().get();
+            CrudRepository<T> repository = locator.find( entityType );
+            StreamSupport
+                .stream( repository.findAll().spliterator(), false )
+                .map( entity -> entity.identity().get() )
+                .map( identity -> resourceBuilder.createEntityRef( identity, base ) )
+                .forEach( result::add );
+            return result;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/9304d008/libraries/restlet/src/main/java/org/apache/zest/library/restlet/crud/EntityRef.java
----------------------------------------------------------------------
diff --git a/libraries/restlet/src/main/java/org/apache/zest/library/restlet/crud/EntityRef.java b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/crud/EntityRef.java
new file mode 100644
index 0000000..b3ea8df
--- /dev/null
+++ b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/crud/EntityRef.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.zest.library.restlet.crud;
+
+import org.apache.zest.api.property.Property;
+import org.apache.zest.library.restlet.RestLink;
+
+public interface EntityRef
+{
+    Property<String> name();
+
+    Property<RestLink> get();
+
+    Property<RestLink> put();
+
+    Property<RestLink> delete();
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/9304d008/libraries/restlet/src/main/java/org/apache/zest/library/restlet/crud/EntityResource.java
----------------------------------------------------------------------
diff --git a/libraries/restlet/src/main/java/org/apache/zest/library/restlet/crud/EntityResource.java b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/crud/EntityResource.java
new file mode 100644
index 0000000..ca7cafe
--- /dev/null
+++ b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/crud/EntityResource.java
@@ -0,0 +1,228 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.zest.library.restlet.crud;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.TemporalAccessor;
+import org.apache.zest.api.common.Optional;
+import org.apache.zest.api.entity.Identity;
+import org.apache.zest.api.injection.scope.Service;
+import org.apache.zest.api.injection.scope.Structure;
+import org.apache.zest.api.injection.scope.This;
+import org.apache.zest.api.mixin.Mixins;
+import org.apache.zest.api.property.Property;
+import org.apache.zest.api.value.ValueBuilder;
+import org.apache.zest.api.value.ValueBuilderFactory;
+import org.apache.zest.library.restlet.FormField;
+import org.apache.zest.library.restlet.RestForm;
+import org.apache.zest.library.restlet.RestLink;
+import org.apache.zest.library.restlet.repository.RepositoryLocator;
+import org.apache.zest.library.restlet.resource.ResourceBuilder;
+import org.apache.zest.library.restlet.resource.ServerResource;
+import org.apache.zest.spi.ZestSPI;
+import org.restlet.data.Reference;
+
+@Mixins( EntityResource.Mixin.class )
+public interface EntityResource<T extends Identity> extends ServerResource<T>
+{
+    interface EntityParam
+    {
+        @Optional
+        Property<String> invoke();
+    }
+
+    abstract class Mixin<T extends Identity>
+        implements EntityResource<T>
+    {
+
+        @Structure
+        private ZestSPI spi;
+
+        @Structure
+        private ValueBuilderFactory vbf;
+
+        @This
+        private Identity me;
+
+        @This
+        private Parameters<T> parameters;
+
+        @This
+        private EntityParam entityParam;
+
+        @Service
+        private RepositoryLocator locator;
+
+        @Service
+        private ResourceBuilder resourceBuilder;
+
+        @Override
+        public T get()
+        {
+            Class entityType = parameters.entityType().get();
+            //noinspection unchecked
+            return (T) locator.find( entityType ).get( identity() );
+        }
+
+        @Override
+        public void put( T value )
+        {
+            Class<T> entityType = parameters.entityType().get();
+            locator.find( entityType ).update( value );
+        }
+
+        @Override
+        public void delete()
+        {
+            Class entityType = parameters.entityType().get();
+            String nameOfEntity = parameters.id().get();
+            locator.find( entityType ).delete( nameOfEntity );
+        }
+
+        @Override
+        public RestLink post( RestForm form )
+        {
+            Class<T> type = parameters.entityType().get();
+            String methodName = entityParam.invoke().get();
+            try
+            {
+                Method method = findMethod( type, methodName );
+                if( method == null ) // no arg method doesn't exist, look for single arg method
+                {
+                    throw new IllegalArgumentException( "Method '" + methodName + "' is not present on " + type.getName() );
+                }
+                if( method.getParameterCount() == 1 )
+                {
+                    Class entityType = parameters.entityType().get();
+                    //noinspection unchecked
+                    T entity = (T) locator.find( entityType ).get( identity() );
+
+                    Class argType = method.getParameterTypes()[ 0 ];
+                    Object parameters = createParametersComposite( form, argType );
+                    method.invoke( entity, parameters );
+                }
+                else
+                {
+                    method.invoke( me );
+                }
+            }
+            catch( Exception e )
+            {
+                String message = e.getMessage();
+                while( e instanceof InvocationTargetException )
+                {
+                    e = (Exception) ( (InvocationTargetException) e ).getTargetException();
+                    message = e.getMessage();
+                }
+                throw new RuntimeException( message, e );
+            }
+            Reference base = parameters.request().get().getResourceRef();
+            return resourceBuilder.createRestLink( "", base, org.restlet.data.Method.GET );
+        }
+
+        private Object createParametersComposite( RestForm form, Class argType )
+        {
+            ValueBuilder<?> vb = vbf.newValueBuilderWithState(
+                argType,
+                descriptor -> {
+                    FormField field = form.field( descriptor.qualifiedName().name() );
+                    if( field == null )
+                    {
+                        return null;
+                    }
+                    Class<?> propertyType = descriptor.valueType().mainType();
+                    Property<String> value = field.value();
+                    if( value == null )
+                    {
+                        return null;
+                    }
+                    return convertPropertyValue( value.get(), propertyType );
+                },
+                descriptor -> null,
+                descriptor -> null,
+                descriptor -> null
+            );
+            return vb.newInstance();
+        }
+
+        private Method findMethod( Class<T> type, String methodName )
+        {
+            Method[] methods = type.getMethods();
+            Method method = null;
+            for( Method m : methods )
+            {
+                if( m.getName().equals( methodName ) )
+                {
+                    method = m;
+                    break;
+                }
+            }
+            return method;
+        }
+
+        private Object convertPropertyValue( String input, Class<?> propertyType )
+        {
+            if( propertyType.equals( String.class ) )
+            {
+                return input;
+            }
+            if( Integer.class.isAssignableFrom( propertyType ) )
+            {
+                return Integer.parseInt( input );
+            }
+            if( Boolean.class.isAssignableFrom( propertyType ) )
+            {
+                return Boolean.valueOf( input );
+            }
+            if( Double.class.isAssignableFrom( propertyType ) )
+            {
+                return Double.parseDouble( input );
+            }
+            if( Long.class.isAssignableFrom( propertyType ) )
+            {
+                return Long.parseLong( input );
+            }
+            if( Byte.class.isAssignableFrom( propertyType ) )
+            {
+                return Byte.parseByte( input );
+            }
+            if( Short.class.isAssignableFrom( propertyType ) )
+            {
+                return Short.parseShort( input );
+            }
+            if( Float.class.isAssignableFrom( propertyType ) )
+            {
+                return Float.parseFloat( input );
+            }
+            if( Character.class.isAssignableFrom( propertyType ) )
+            {
+                return input.charAt( 0 );
+            }
+            if( TemporalAccessor.class.isAssignableFrom( propertyType ) )
+            {
+                return DateTimeFormatter.ISO_DATE_TIME.parse( input );
+            }
+            return null;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/9304d008/libraries/restlet/src/main/java/org/apache/zest/library/restlet/crud/IdentitySpecification.java
----------------------------------------------------------------------
diff --git a/libraries/restlet/src/main/java/org/apache/zest/library/restlet/crud/IdentitySpecification.java b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/crud/IdentitySpecification.java
new file mode 100644
index 0000000..9aefbff
--- /dev/null
+++ b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/crud/IdentitySpecification.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.zest.library.restlet.crud;
+
+import org.apache.zest.api.entity.Identity;
+import org.apache.zest.api.util.NullArgumentException;
+import org.apache.zest.functional.Specification;
+import org.apache.zest.library.restlet.HasName;
+
+public class IdentitySpecification
+    implements Specification<Identity>
+{
+    private final String id;
+
+    public IdentitySpecification( String identity )
+    {
+        NullArgumentException.validateNotNull( "identity", identity );
+        this.id = identity;
+    }
+
+    @Override
+    public boolean satisfiedBy( Identity item )
+    {
+        return id.equals( item.identity().get() );
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/9304d008/libraries/restlet/src/main/java/org/apache/zest/library/restlet/crud/RepositoryNotFoundException.java
----------------------------------------------------------------------
diff --git a/libraries/restlet/src/main/java/org/apache/zest/library/restlet/crud/RepositoryNotFoundException.java b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/crud/RepositoryNotFoundException.java
new file mode 100644
index 0000000..a4f9416
--- /dev/null
+++ b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/crud/RepositoryNotFoundException.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.zest.library.restlet.crud;
+
+public class RepositoryNotFoundException extends RuntimeException
+{
+    public RepositoryNotFoundException( Class entityType )
+    {
+        super( "No repository found for " + entityType.getName() );
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/9304d008/libraries/restlet/src/main/java/org/apache/zest/library/restlet/filters/NameFilter.java
----------------------------------------------------------------------
diff --git a/libraries/restlet/src/main/java/org/apache/zest/library/restlet/filters/NameFilter.java b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/filters/NameFilter.java
new file mode 100644
index 0000000..494e385
--- /dev/null
+++ b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/filters/NameFilter.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.zest.library.restlet.filters;
+
+import java.util.function.Predicate;
+import org.apache.zest.library.restlet.FormField;
+import org.apache.zest.library.restlet.HasName;
+
+public class NameFilter implements Predicate<HasName>
+{
+    private final String name;
+
+    public NameFilter( String name )
+    {
+        this.name = name;
+    }
+
+    @Override
+    public boolean test( HasName hasName )
+    {
+        return hasName.name().get().equals(name);
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/9304d008/libraries/restlet/src/main/java/org/apache/zest/library/restlet/identity/CanonicalName.java
----------------------------------------------------------------------
diff --git a/libraries/restlet/src/main/java/org/apache/zest/library/restlet/identity/CanonicalName.java b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/identity/CanonicalName.java
new file mode 100644
index 0000000..622e633
--- /dev/null
+++ b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/identity/CanonicalName.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.zest.library.restlet.identity;
+
+import org.apache.zest.api.entity.Identity;
+import org.apache.zest.api.injection.scope.Service;
+import org.apache.zest.api.injection.scope.This;
+import org.apache.zest.api.mixin.Mixins;
+
+@Mixins( CanonicalName.Mixin.class )
+public interface CanonicalName
+{
+    String name();
+
+    Class type();
+
+    class Mixin
+        implements CanonicalName
+    {
+        @This
+        private Identity me;
+
+        @Service
+        private IdentityManager manager;
+
+        @Override
+        public String name()
+        {
+            return manager.extractName( me.identity().get() );
+        }
+
+        @Override
+        public Class type()
+        {
+            return manager.extractType( me.identity().get() );
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/9304d008/libraries/restlet/src/main/java/org/apache/zest/library/restlet/identity/IdentityManager.java
----------------------------------------------------------------------
diff --git a/libraries/restlet/src/main/java/org/apache/zest/library/restlet/identity/IdentityManager.java b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/identity/IdentityManager.java
new file mode 100644
index 0000000..8f891f0
--- /dev/null
+++ b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/identity/IdentityManager.java
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.zest.library.restlet.identity;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import org.apache.zest.api.common.Optional;
+import org.apache.zest.api.concern.Concerns;
+import org.apache.zest.api.configuration.Configuration;
+import org.apache.zest.api.injection.scope.Service;
+import org.apache.zest.api.injection.scope.This;
+import org.apache.zest.api.mixin.Mixins;
+import org.apache.zest.api.unitofwork.concern.UnitOfWorkConcern;
+import org.apache.zest.api.unitofwork.concern.UnitOfWorkPropagation;
+import org.apache.zest.functional.Specification;
+import org.apache.zest.spi.uuid.UuidIdentityGeneratorService;
+
+import static org.apache.zest.functional.Iterables.filter;
+import static org.apache.zest.functional.Iterables.first;
+
+@Mixins( IdentityManager.Mixin.class )
+@Concerns( { UnitOfWorkConcern.class } )
+public interface IdentityManager
+{
+    char SEPARATOR = '~';
+    String IDENTITY_SIGNATURE = "[0-9][0-9]*~.*";
+
+    boolean isIdentity( String candidate );
+
+    String generate( Class type, @Optional String canonicalName );
+
+    String extractName( String identity );
+
+    Class extractType( String identity );
+
+    @UnitOfWorkPropagation
+    String findPrefix( Class type );
+
+    class Mixin
+        implements IdentityManager
+    {
+        @Service
+        private UuidIdentityGeneratorService uuidService;
+
+        @This
+        private Configuration<IdentityMappingConfiguration> config;
+
+        private ConcurrentHashMap<String, Class> actualClasses = new ConcurrentHashMap<>();
+
+        @Override
+        public boolean isIdentity( String candidate )
+        {
+            return candidate.matches( IDENTITY_SIGNATURE );
+        }
+
+        @Override
+        public String generate( Class type, String canonicalName )
+        {
+            if( canonicalName == null )
+            {
+                canonicalName = uuidService.generate( type );
+            }
+            if( isIdentity( canonicalName ) )
+            {
+                // This is already an ID, and we simply return it.
+                return canonicalName;
+            }
+            String prefix = findPrefix( type );
+            return prefix + SEPARATOR + canonicalName;
+        }
+
+        @Override
+        public String findPrefix( Class type )
+        {
+            Map<String, String> mapping = config.get().mapping().get();
+            String prefix = mapping.get( type.getName() );
+            if( prefix == null )
+            {
+                config.refresh();
+                mapping = config.get().mapping().get();
+                prefix = Integer.toString( mapping.size() + 1 );
+                mapping.put( type.getName(), prefix );
+                config.get().mapping().set( mapping );
+                config.save();
+            }
+            actualClasses.put( type.getName(), type );
+            return prefix;
+        }
+
+        @Override
+        public String extractName( String identity )
+        {
+            if( !isIdentity( identity ) )
+            {
+                return identity;
+            }
+            int pos = identity.indexOf( SEPARATOR );
+            if( pos < 1 )
+            {
+                throw new InvalidIdentityFormatException( identity );
+            }
+            return identity.substring( pos + 1 );
+        }
+
+        @Override
+        public Class extractType( String identity )
+        {
+            if( isIdentity( identity ) )
+            {
+                throw new IllegalArgumentException( "Given argument '" + identity + "' is not an Identity" );
+            }
+            int pos = identity.indexOf( SEPARATOR );
+            if( pos < 1 )
+            {
+                throw new InvalidIdentityFormatException( identity );
+            }
+            String prefix = identity.substring( 0, pos );
+            Map.Entry<String, String> found =
+                first(
+                    filter(
+                        new FindClassSpecification( prefix ),
+                        config.get().mapping().get().entrySet()
+                    )
+                );
+            return found == null ? null : actualClasses.get( found.getKey() );
+        }
+
+        private static class FindClassSpecification
+            implements Specification<Map.Entry<Class, String>>
+        {
+            private String prefix;
+
+            private FindClassSpecification( String prefix )
+            {
+                this.prefix = prefix;
+            }
+
+            @Override
+            public boolean satisfiedBy( Map.Entry<Class, String> item )
+            {
+                return item.getValue().equals( prefix );
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/9304d008/libraries/restlet/src/main/java/org/apache/zest/library/restlet/identity/IdentityMappingConfiguration.java
----------------------------------------------------------------------
diff --git a/libraries/restlet/src/main/java/org/apache/zest/library/restlet/identity/IdentityMappingConfiguration.java b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/identity/IdentityMappingConfiguration.java
new file mode 100644
index 0000000..8b2fb55
--- /dev/null
+++ b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/identity/IdentityMappingConfiguration.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.zest.library.restlet.identity;
+
+import java.util.Map;
+import org.apache.zest.api.common.Optional;
+import org.apache.zest.api.common.UseDefaults;
+import org.apache.zest.api.property.Property;
+
+public interface IdentityMappingConfiguration
+{
+    @Optional
+    @UseDefaults
+    Property<Map<String, String>> mapping();
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/9304d008/libraries/restlet/src/main/java/org/apache/zest/library/restlet/identity/InvalidIdentityFormatException.java
----------------------------------------------------------------------
diff --git a/libraries/restlet/src/main/java/org/apache/zest/library/restlet/identity/InvalidIdentityFormatException.java b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/identity/InvalidIdentityFormatException.java
new file mode 100644
index 0000000..9e3e8b6
--- /dev/null
+++ b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/identity/InvalidIdentityFormatException.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.zest.library.restlet.identity;
+
+public class InvalidIdentityFormatException extends RuntimeException
+{
+    public InvalidIdentityFormatException( String identity )
+    {
+        super( "The given identity wasn't generated by the UrlIdentityManager: " + identity );
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/9304d008/libraries/restlet/src/main/java/org/apache/zest/library/restlet/metainfo/UserIdentity.java
----------------------------------------------------------------------
diff --git a/libraries/restlet/src/main/java/org/apache/zest/library/restlet/metainfo/UserIdentity.java b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/metainfo/UserIdentity.java
new file mode 100644
index 0000000..aaefc2d
--- /dev/null
+++ b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/metainfo/UserIdentity.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.zest.library.restlet.metainfo;
+
+public class UserIdentity
+{
+    private final String identifier;
+    private final String name;
+    private final String email;
+    private final String firstName;
+    private final String lastName;
+
+    public UserIdentity( String identifier, String name, String email, String firstName, String lastName )
+    {
+        this.identifier = identifier;
+        this.name = name;
+        this.email = email;
+        this.firstName = firstName;
+        this.lastName = lastName;
+    }
+
+    public String identifier()
+    {
+        return identifier;
+    }
+
+    public String name()
+    {
+        return name;
+    }
+
+    public String email()
+    {
+        return email;
+    }
+
+    public String firstName()
+    {
+        return firstName;
+    }
+
+    public String lastName()
+    {
+        return lastName;
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/9304d008/libraries/restlet/src/main/java/org/apache/zest/library/restlet/repository/CrudRepository.java
----------------------------------------------------------------------
diff --git a/libraries/restlet/src/main/java/org/apache/zest/library/restlet/repository/CrudRepository.java b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/repository/CrudRepository.java
new file mode 100644
index 0000000..90c59ee
--- /dev/null
+++ b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/repository/CrudRepository.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.zest.library.restlet.repository;
+
+import org.apache.zest.api.entity.Identity;
+import org.apache.zest.api.unitofwork.concern.UnitOfWorkPropagation;
+import org.apache.zest.functional.Specification;
+
+public interface CrudRepository<T extends Identity>
+{
+    @UnitOfWorkPropagation
+    void create( @EntityName String nameOfEntity );
+
+    @UnitOfWorkPropagation
+    T get( @EntityName String nameOfEntity );
+
+    @UnitOfWorkPropagation
+    void update( T newStateAsValue );
+
+    @UnitOfWorkPropagation
+    void delete( @EntityName String nameOfEntity );
+
+    @UnitOfWorkPropagation
+    Iterable<T> findAll();
+
+    @UnitOfWorkPropagation
+    Iterable<T> find( Specification specification );
+
+    T toValue( T entity );
+
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/9304d008/libraries/restlet/src/main/java/org/apache/zest/library/restlet/repository/EntityName.java
----------------------------------------------------------------------
diff --git a/libraries/restlet/src/main/java/org/apache/zest/library/restlet/repository/EntityName.java b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/repository/EntityName.java
new file mode 100644
index 0000000..8ee6c06
--- /dev/null
+++ b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/repository/EntityName.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.zest.library.restlet.repository;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import org.apache.zest.api.constraint.ConstraintDeclaration;
+import org.apache.zest.api.constraint.Constraints;
+import org.apache.zest.library.restlet.identity.IdentityManager;
+
+@ConstraintDeclaration
+@Retention( RetentionPolicy.RUNTIME )
+@Constraints( EntityName.Constraint.class )
+public @interface EntityName
+{
+
+    class Constraint
+        implements org.apache.zest.api.constraint.Constraint<EntityName, String>
+    {
+
+        @Override
+        public boolean isValid( EntityName annotation, String value )
+        {
+            int pos = value.indexOf( IdentityManager.SEPARATOR );
+            if( pos > 0 )
+            {
+                value = value.substring( pos+1 );
+            }
+            for( int i = 0; i < value.length(); i++ )
+            {
+                char ch = value.charAt( i );
+                if( ( ch < 'A' || ch > 'Z' ) &&
+                    ( ch < 'a' || ch > 'z' ) &&
+                    ( ch < '0' || ch > '9' ) &&
+                    ( ch != '.' ) &&
+                    ( ch != '_' ) &&
+                    ( ch != '-' )
+                    )
+                {
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/9304d008/libraries/restlet/src/main/java/org/apache/zest/library/restlet/repository/EntityTypeDescriptor.java
----------------------------------------------------------------------
diff --git a/libraries/restlet/src/main/java/org/apache/zest/library/restlet/repository/EntityTypeDescriptor.java b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/repository/EntityTypeDescriptor.java
new file mode 100644
index 0000000..e14848c
--- /dev/null
+++ b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/repository/EntityTypeDescriptor.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.zest.library.restlet.repository;
+
+public class EntityTypeDescriptor
+{
+    private Class entityType;
+
+    public EntityTypeDescriptor( Class entityType )
+    {
+        this.entityType = entityType;
+    }
+
+    public Class entityType()
+    {
+        return entityType;
+    }
+
+    @Override
+    public String toString()
+    {
+        return "EntityTypeDescriptor{" + entityType + '}';
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/9304d008/libraries/restlet/src/main/java/org/apache/zest/library/restlet/repository/MissingRepositoryException.java
----------------------------------------------------------------------
diff --git a/libraries/restlet/src/main/java/org/apache/zest/library/restlet/repository/MissingRepositoryException.java b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/repository/MissingRepositoryException.java
new file mode 100644
index 0000000..ceafcdc
--- /dev/null
+++ b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/repository/MissingRepositoryException.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.zest.library.restlet.repository;
+
+public class MissingRepositoryException extends RuntimeException
+{
+    public MissingRepositoryException( Class typeName )
+    {
+        super( "There is no Entity Repository @Tagged with the name '" + typeName + "'" );
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/9304d008/libraries/restlet/src/main/java/org/apache/zest/library/restlet/repository/RepositoryLocator.java
----------------------------------------------------------------------
diff --git a/libraries/restlet/src/main/java/org/apache/zest/library/restlet/repository/RepositoryLocator.java b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/repository/RepositoryLocator.java
new file mode 100644
index 0000000..0f2dd1f
--- /dev/null
+++ b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/repository/RepositoryLocator.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.zest.library.restlet.repository;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.zest.api.entity.Identity;
+import org.apache.zest.api.injection.scope.Service;
+import org.apache.zest.api.mixin.Mixins;
+import org.apache.zest.api.service.ServiceReference;
+
+import static org.apache.zest.functional.ForEach.forEach;
+
+@Mixins( RepositoryLocator.Mixin.class )
+public interface RepositoryLocator
+{
+    <T extends Identity> CrudRepository<T> find( Class<T> entityType );
+
+    class Mixin
+        implements RepositoryLocator
+    {
+
+        private Map<Class, CrudRepository> repositories = new HashMap<>();
+
+        public Mixin( @Service Iterable<ServiceReference<CrudRepository>> repositories )
+        {
+            forEach( repositories ).forEach( ref -> {
+                Class type = ref.metaInfo( EntityTypeDescriptor.class ).entityType();
+                this.repositories.put( type, ref.get() );
+            } );
+        }
+
+        @Override
+        public <T extends Identity> CrudRepository<T> find( Class<T> entityType )
+        {
+            //noinspection unchecked
+            return repositories.get( entityType );
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/9304d008/libraries/restlet/src/main/java/org/apache/zest/library/restlet/repository/SmallCrudRepositoryMixin.java
----------------------------------------------------------------------
diff --git a/libraries/restlet/src/main/java/org/apache/zest/library/restlet/repository/SmallCrudRepositoryMixin.java b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/repository/SmallCrudRepositoryMixin.java
new file mode 100644
index 0000000..c18fcfc
--- /dev/null
+++ b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/repository/SmallCrudRepositoryMixin.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.zest.library.restlet.repository;
+
+import org.apache.zest.api.ZestAPI;
+import org.apache.zest.api.entity.Identity;
+import org.apache.zest.api.injection.scope.Service;
+import org.apache.zest.api.injection.scope.Structure;
+import org.apache.zest.api.injection.scope.This;
+import org.apache.zest.api.query.Query;
+import org.apache.zest.api.query.QueryBuilder;
+import org.apache.zest.api.query.QueryBuilderFactory;
+import org.apache.zest.api.service.ServiceComposite;
+import org.apache.zest.api.unitofwork.EntityTypeNotFoundException;
+import org.apache.zest.api.unitofwork.NoSuchEntityException;
+import org.apache.zest.api.unitofwork.UnitOfWork;
+import org.apache.zest.api.unitofwork.UnitOfWorkFactory;
+import org.apache.zest.functional.Specification;
+import org.apache.zest.functional.Specifications;
+import org.apache.zest.library.restlet.identity.IdentityManager;
+
+public class SmallCrudRepositoryMixin<T extends Identity>
+    implements CrudRepository<T>
+{
+    @Structure
+    private UnitOfWorkFactory uowf;
+
+    @Structure
+    private QueryBuilderFactory qbf;
+
+    @Service
+    private IdentityManager identityManager;
+
+    private final Class<T> entityType;
+
+    @SuppressWarnings( "unchecked" )
+    public SmallCrudRepositoryMixin( @Structure ZestAPI api, @This ServiceComposite me )
+    {
+        entityType = api.serviceDescriptorFor( me ).metaInfo( EntityTypeDescriptor.class ).entityType();
+    }
+
+    @Override
+    public void create( String identity )
+    {
+        UnitOfWork uow = uowf.currentUnitOfWork();
+        uow.newEntity( entityType, identity );
+    }
+
+    @Override
+    public T get( String id )
+    {
+        UnitOfWork uow = uowf.currentUnitOfWork();
+        return uow.get( entityType, id );
+    }
+
+    @Override
+    public void update( T newStateAsValue )
+    {
+        UnitOfWork uow = uowf.currentUnitOfWork();
+
+        @SuppressWarnings( "unchecked" )
+        Class<Identity> type = (Class<Identity>) entityType;
+
+        uow.toEntity( type, newStateAsValue );  //updates the identified entity with the value
+    }
+
+    @Override
+    public void delete( String nameToDelete )
+    {
+        String id = identityManager.generate( entityType, nameToDelete );
+        UnitOfWork uow = uowf.currentUnitOfWork();
+        try
+        {
+            T entity = uow.get( entityType, id );
+            uow.remove( entity );
+        }
+        catch( NoSuchEntityException | EntityTypeNotFoundException e )
+        {
+            throw new IllegalArgumentException( "Entity  '" + nameToDelete + "' doesn't exist." );
+        }
+    }
+
+    @Override
+    public Iterable<T> findAll()
+    {
+        return find( Specifications.TRUE() );
+    }
+
+    @Override
+    @SuppressWarnings( "unchecked" )
+    public Iterable<T> find( Specification specification )
+    {
+        UnitOfWork uow = uowf.currentUnitOfWork();
+        QueryBuilder<T> qb = qbf.newQueryBuilder( entityType );
+        Query<T> query = uow.newQuery( qb );
+        return qbf.newQueryBuilder( entityType ).where( specification ).newQuery( query );
+    }
+
+    @Override
+    public T toValue( T entity )
+    {
+        return uowf.currentUnitOfWork().toValue( entityType, entity );
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/9304d008/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/CreationParameterized.java
----------------------------------------------------------------------
diff --git a/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/CreationParameterized.java b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/CreationParameterized.java
new file mode 100644
index 0000000..fb9d29b
--- /dev/null
+++ b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/CreationParameterized.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.zest.library.restlet.resource;
+
+public interface CreationParameterized<T>
+{
+    /**
+     * Returns the ValueComposite type to be used to parameterize the reosource.
+     *
+     * @return A type to instantiate as a ValueComposite
+     */
+    Class<T> parametersType();
+
+    void parameterize( T parameters );
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/9304d008/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/CreationResource.java
----------------------------------------------------------------------
diff --git a/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/CreationResource.java b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/CreationResource.java
new file mode 100644
index 0000000..5591793
--- /dev/null
+++ b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/CreationResource.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.zest.library.restlet.resource;
+
+import org.apache.zest.api.entity.Identity;
+import org.apache.zest.api.injection.scope.Service;
+import org.apache.zest.api.injection.scope.Structure;
+import org.apache.zest.api.injection.scope.This;
+import org.apache.zest.api.mixin.Mixins;
+import org.apache.zest.api.property.PropertyDescriptor;
+import org.apache.zest.api.unitofwork.UnitOfWork;
+import org.apache.zest.api.unitofwork.UnitOfWorkFactory;
+import org.apache.zest.api.value.ValueBuilder;
+import org.apache.zest.api.value.ValueBuilderFactory;
+import org.apache.zest.library.restlet.FormField;
+import org.apache.zest.library.restlet.RestForm;
+import org.apache.zest.library.restlet.RestLink;
+import org.apache.zest.library.restlet.identity.IdentityManager;
+import org.apache.zest.library.restlet.repository.RepositoryLocator;
+import org.restlet.data.Method;
+
+@Mixins( CreationResource.CreateHostMixin.class )
+public interface CreationResource<T extends Identity> extends ServerResource<T>
+{
+    abstract class CreateHostMixin<T extends Identity>
+        implements CreationResource<T>
+    {
+        @Structure
+        private ValueBuilderFactory vbf;
+
+        @Structure
+        private UnitOfWorkFactory uowf;
+
+        @This
+        private Parameters<T> parameters;
+
+        @Service
+        private ResourceBuilder resourceBuilder;
+
+        @Service
+        private IdentityManager identityManager;
+
+        @Service
+        private RepositoryLocator locator;
+
+        @Override
+        public RestLink post( RestForm form )
+        {
+            String name = form.field( "name" ).value().get();
+            Class entityType = parameters.entityType().get();
+            String identity = identityManager.generate( entityType, name );
+            locator.find( entityType ).create( identity );
+            doParameterization( form, entityType, identity );
+            return resourceBuilder.createRestLink( name, parameters.request().get().getResourceRef(), Method.GET );
+        }
+
+        private <P> void doParameterization( RestForm form, Class entityType, String identity )
+        {
+            if( !CreationParameterized.class.isAssignableFrom( entityType ) )
+            {
+                return;
+            }
+            //noinspection unchecked
+            CreationParameterized<P> created = (CreationParameterized<P>) locator.find( entityType ).get( identity );
+            P parameterization = createParameterizationValue( form, created );
+            created.parameterize( parameterization );
+        }
+
+        private <V> V createParameterizationValue( final RestForm form, CreationParameterized<V> created )
+        {
+            Class<V> valueType = created.parametersType();
+            ValueBuilder<V> vb = vbf.newValueBuilderWithState(
+                valueType,
+                propertyName -> mapField( form, propertyName ),
+                association -> null,
+                association -> null,
+                association -> null
+            );
+            return vb.newInstance();
+        }
+
+        private Object mapField( RestForm form, PropertyDescriptor propertyName )
+        {
+            String name = propertyName.qualifiedName().name();
+            FormField field = form.field( name );
+            if( field == null )
+            {
+                UnitOfWork uow = uowf.currentUnitOfWork();
+                String usecase = "";
+                if( uow != null )
+                {
+                    usecase = uow.usecase().name();
+                }
+                throw new IllegalArgumentException( "Field named '" + name + "' is required and not present in usecase " + usecase );
+            }
+            return field.value().get();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/9304d008/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/DefaultResourceFactoryImpl.java
----------------------------------------------------------------------
diff --git a/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/DefaultResourceFactoryImpl.java b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/DefaultResourceFactoryImpl.java
new file mode 100644
index 0000000..af6f07e
--- /dev/null
+++ b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/DefaultResourceFactoryImpl.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.zest.library.restlet.resource;
+
+import java.util.Map;
+import org.apache.zest.api.entity.Identity;
+import org.apache.zest.api.injection.scope.Structure;
+import org.apache.zest.api.injection.scope.Uses;
+import org.apache.zest.api.property.PropertyDescriptor;
+import org.apache.zest.api.value.ValueBuilder;
+import org.apache.zest.api.value.ValueBuilderFactory;
+import org.apache.zest.spi.ZestSPI;
+import org.restlet.Context;
+import org.restlet.Request;
+import org.restlet.Response;
+import org.restlet.routing.Router;
+
+public class DefaultResourceFactoryImpl<K extends Identity, T extends ServerResource<K>>
+    implements ResourceFactory<K, T>
+{
+    @Uses
+    private Class<T> resourceType;
+
+    @Uses
+    private Context context;
+
+    @Uses
+    private Router router;
+
+    @Uses
+    private Request request;
+
+    @Structure
+    private ValueBuilderFactory vbf;
+
+    @Structure
+    private ZestSPI spi;
+
+    @Override
+    public T create( Class<T> entityType, Request request, Response response, Context context )
+    {
+        final Map<String, Object> attributes = request.getAttributes();
+        String id = (String) attributes.get( "id" );
+
+        ValueBuilder<T> builder = vbf.newValueBuilderWithState(
+            resourceType,
+            descriptor -> findValue( attributes, descriptor ),
+            descriptor -> null,
+            descriptor -> null,
+            descriptor -> null
+        );
+        //noinspection unchecked
+        ServerResource.Parameters<T> params = builder.prototypeFor( ServerResource.Parameters.class );
+        params.id().set( id );
+        params.entityType().set( entityType );
+        params.context().set( this.context );
+        params.request().set( request );
+        params.router().set( router );
+        params.response().set( response );
+        return builder.newInstance();
+    }
+
+    private Object findValue( Map<String, Object> attributes, PropertyDescriptor descriptor )
+    {
+        String name = descriptor.qualifiedName().name();
+        if( name.equals( "identity" ) )
+        {
+            Object id = attributes.get( "id" );
+            if( id == null )
+            {
+                throw new IllegalArgumentException( resourceType.getName() + " implements Identity and must have an {id} attribute in the path templatee." );
+            }
+            return id;
+        }
+        return attributes.get( name );
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/9304d008/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/EntryPoint.java
----------------------------------------------------------------------
diff --git a/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/EntryPoint.java b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/EntryPoint.java
new file mode 100644
index 0000000..1072cc8
--- /dev/null
+++ b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/EntryPoint.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.zest.library.restlet.resource;
+
+import java.util.Map;
+import org.apache.zest.api.entity.Identity;
+import org.apache.zest.api.property.Property;
+import org.apache.zest.library.restlet.RestLink;
+
+public interface EntryPoint extends Identity
+{
+    Property<Map<String,RestLink>> api();
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/9304d008/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/EntryPointResource.java
----------------------------------------------------------------------
diff --git a/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/EntryPointResource.java b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/EntryPointResource.java
new file mode 100644
index 0000000..3a3667b
--- /dev/null
+++ b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/EntryPointResource.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.zest.library.restlet.resource;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.zest.api.injection.scope.Service;
+import org.apache.zest.api.injection.scope.Structure;
+import org.apache.zest.api.injection.scope.This;
+import org.apache.zest.api.mixin.Mixins;
+import org.apache.zest.api.value.ValueBuilder;
+import org.apache.zest.api.value.ValueBuilderFactory;
+import org.apache.zest.library.restlet.RestLink;
+import org.restlet.data.Method;
+import org.restlet.data.Reference;
+import org.restlet.routing.Route;
+import org.restlet.routing.Template;
+import org.restlet.routing.TemplateRoute;
+
+@Mixins( EntryPointResource.Mixin.class )
+public interface EntryPointResource extends ServerResource<EntryPoint>
+{
+    abstract class Mixin
+        implements EntryPointResource
+    {
+        @This
+        private Parameters<EntryPoint> parameters;
+
+        @Service
+        private ResourceBuilder resourceBuilder;
+
+        @Structure
+        private ValueBuilderFactory vbf;
+
+        private EntryPoint entryPoint;
+
+        @Override
+        public EntryPoint get()
+        {
+            if( entryPoint == null )
+            {
+                entryPoint = createEntryPoint();
+            }
+            return entryPoint;
+        }
+
+        private EntryPoint createEntryPoint()
+        {
+            Map<String, RestLink> entryPoints = new HashMap<>();
+            for( Route r : parameters.router().get().getRoutes() )
+            {
+                if( r instanceof TemplateRoute)
+                {
+                    TemplateRoute route = (TemplateRoute) r;
+                    Template template = route.getTemplate();
+                    // Only include patterns that doesn't have variables, and has a proper name.
+                    if( template.getVariableNames().size() == 0 && route.getName().indexOf( '>' ) == -1 )
+                    {
+                        Reference hostRef = parameters.request().get().getOriginalRef();
+                        Reference reference = new Reference( hostRef, template.getPattern() );
+                        RestLink link;
+                        if( route.getDescription() == null )
+                        {
+                            link = resourceBuilder.createRestLink( route.getName(), reference, Method.GET );
+                        }
+                        else
+                        {
+                            link = resourceBuilder.createRestLink( route.getName(), reference, Method.GET, route.getDescription() );
+                        }
+                        entryPoints.put( route.getName(), link );
+                    }
+                }
+            }
+            ValueBuilder<EntryPoint> builder = vbf.newValueBuilder( EntryPoint.class );
+            builder.prototype().identity().set( "/" );
+            builder.prototype().api().set( entryPoints );
+            return builder.newInstance();
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/zest-java/blob/9304d008/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/NotPresentException.java
----------------------------------------------------------------------
diff --git a/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/NotPresentException.java b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/NotPresentException.java
new file mode 100644
index 0000000..570471f
--- /dev/null
+++ b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/NotPresentException.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.zest.library.restlet.resource;
+
+public class NotPresentException extends RuntimeException
+{
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/9304d008/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/Parameterizer.java
----------------------------------------------------------------------
diff --git a/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/Parameterizer.java b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/Parameterizer.java
new file mode 100644
index 0000000..8735256
--- /dev/null
+++ b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/Parameterizer.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.zest.library.restlet.resource;
+
+public interface Parameterizer
+{
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/9304d008/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/ResourceBuilder.java
----------------------------------------------------------------------
diff --git a/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/ResourceBuilder.java b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/ResourceBuilder.java
new file mode 100644
index 0000000..37bb53e
--- /dev/null
+++ b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/ResourceBuilder.java
@@ -0,0 +1,173 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.zest.library.restlet.resource;
+
+import java.io.IOException;
+import java.util.Collections;
+import org.apache.zest.api.entity.Identity;
+import org.apache.zest.api.injection.scope.Service;
+import org.apache.zest.api.injection.scope.Structure;
+import org.apache.zest.api.mixin.Mixins;
+import org.apache.zest.api.object.ObjectFactory;
+import org.apache.zest.api.value.ValueBuilder;
+import org.apache.zest.api.value.ValueBuilderFactory;
+import org.apache.zest.library.restlet.Command;
+import org.apache.zest.library.restlet.FormField;
+import org.apache.zest.library.restlet.RestForm;
+import org.apache.zest.library.restlet.RestLink;
+import org.apache.zest.library.restlet.crud.EntityRef;
+import org.apache.zest.library.restlet.identity.IdentityManager;
+import org.apache.zest.library.restlet.serialization.ZestConverter;
+import org.restlet.data.Method;
+import org.restlet.data.Reference;
+import org.restlet.representation.Representation;
+import org.restlet.representation.Variant;
+import org.restlet.routing.Route;
+import org.restlet.routing.Router;
+
+@Mixins( ResourceBuilder.Mixin.class )
+public interface ResourceBuilder
+{
+    EntityRef createEntityRef( String name, Reference base );
+
+    EntityRef createEntityRef( String name, RestLink get, RestLink put, RestLink delete );
+
+    RestLink createRestLink( String name, Reference base, Method method );
+
+    RestLink createRestLink( String name, Reference base, Method method, String description );
+
+    Command createCommand( Reference base );
+
+    RestForm createNameForm( Reference base );
+
+    FormField createFormField( String name, String type );
+
+    <T extends Identity> Representation toRepresentation( Class<T> type, T composite );
+
+    <T extends Identity> T toObject( Class<T> type, Representation representation )
+        throws IOException;
+
+    Route findRoute( String name, Router router );
+
+    class Mixin
+        implements ResourceBuilder
+    {
+        @Service
+        private IdentityManager identityManager;
+
+        private final ZestConverter converter;
+
+        @Structure
+        private ValueBuilderFactory vbf;
+
+        public Mixin( @Structure ObjectFactory objectFactory )
+        {
+            converter = new ZestConverter( objectFactory );
+        }
+
+        public EntityRef createEntityRef( String identity, Reference base )
+        {
+            String name = identityManager.extractName( identity );
+            RestLink get = createRestLink( name, base, Method.GET );
+            RestLink put = createRestLink( name, base, Method.PUT );
+            RestLink delete = createRestLink( name, base, Method.DELETE );
+            return createEntityRef( name, get, put, delete );
+        }
+
+        public EntityRef createEntityRef( String identity, RestLink get, RestLink put, RestLink delete )
+        {
+            ValueBuilder<EntityRef> refBuilder = vbf.newValueBuilder( EntityRef.class );
+            EntityRef refPrototype = refBuilder.prototype();
+            refPrototype.name().set( identityManager.extractName( identity ) );
+            refPrototype.get().set( get );
+            refPrototype.put().set( put );
+            refPrototype.delete().set( delete );
+            return refBuilder.newInstance();
+        }
+
+        public RestLink createRestLink( String name, Reference base, Method method )
+        {
+            name = identityManager.extractName( name );
+
+            ValueBuilder<RestLink> builder = vbf.newValueBuilder( RestLink.class );
+            RestLink prototype = builder.prototype();
+            String path = base.toUri().resolve( name ).getPath();
+            prototype.path().set( path.endsWith( "/" ) ? path : path + "/" );
+            prototype.method().set( method.getName() );
+            return builder.newInstance();
+        }
+
+        @Override
+        public RestLink createRestLink( String name, Reference base, Method method, String description )
+        {
+            ValueBuilder<RestLink> builder = vbf.newValueBuilder( RestLink.class );
+            RestLink prototype = builder.prototype();
+            prototype.path().set( base.toUri().resolve( name ).getPath() + "/" );
+            prototype.method().set( method.getName() );
+            prototype.description().set( description );
+            return builder.newInstance();
+        }
+
+        public Command createCommand( Reference base )
+        {
+            RestForm form = createNameForm( base );
+            ValueBuilder<Command> builder = vbf.newValueBuilder( Command.class );
+            builder.prototype().name().set( "create" );
+            builder.prototype().form().set( form );
+            return builder.newInstance();
+        }
+
+        public RestForm createNameForm( Reference base )
+        {
+            ValueBuilder<RestForm> builder = vbf.newValueBuilder( RestForm.class );
+            builder.prototype().link().set( createRestLink( "form", base, Method.POST ) );
+            builder.prototype().fields().set( Collections.singletonList( createFormField( "name", FormField.TEXT ) ) );
+            return builder.newInstance();
+        }
+
+        public FormField createFormField( String name, String type )
+        {
+            ValueBuilder<FormField> builder = vbf.newValueBuilder( FormField.class );
+            builder.prototype().name().set( name );
+            builder.prototype().type().set( type );
+            return builder.newInstance();
+        }
+
+        @Override
+        public <T extends Identity> Representation toRepresentation( Class<T> type, T composite )
+        {
+            return converter.toRepresentation( composite, new Variant(), null );
+        }
+
+        @Override
+        public <T extends Identity> T toObject( Class<T> type, Representation representation )
+            throws IOException
+        {
+            return converter.toObject( representation, type, null );
+        }
+
+        @Override
+        public Route findRoute( String name, Router router )
+        {
+            return router.getRoutes().stream().filter( route -> name.equals( route.getName() ) ).findFirst().get();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/9304d008/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/ResourceFactory.java
----------------------------------------------------------------------
diff --git a/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/ResourceFactory.java b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/ResourceFactory.java
new file mode 100644
index 0000000..8f95f6d
--- /dev/null
+++ b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/ResourceFactory.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.zest.library.restlet.resource;
+
+import org.apache.zest.api.entity.Identity;
+import org.restlet.Context;
+import org.restlet.Request;
+import org.restlet.Response;
+
+public interface ResourceFactory<K extends Identity, T extends ServerResource<K>>
+{
+    T create( Class<T> resourceType, Request request, Response response, Context context );
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/9304d008/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/ServerResource.java
----------------------------------------------------------------------
diff --git a/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/ServerResource.java b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/ServerResource.java
new file mode 100644
index 0000000..4e2eef2
--- /dev/null
+++ b/libraries/restlet/src/main/java/org/apache/zest/library/restlet/resource/ServerResource.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.zest.library.restlet.resource;
+
+import org.apache.zest.api.common.Optional;
+import org.apache.zest.api.entity.Identity;
+import org.apache.zest.api.injection.scope.Service;
+import org.apache.zest.api.injection.scope.This;
+import org.apache.zest.api.mixin.Mixins;
+import org.apache.zest.api.property.Property;
+import org.apache.zest.library.restlet.RestForm;
+import org.apache.zest.library.restlet.RestLink;
+import org.apache.zest.library.restlet.identity.IdentityManager;
+import org.restlet.Context;
+import org.restlet.Request;
+import org.restlet.Response;
+import org.restlet.routing.Router;
+
+@Mixins( { ServerResource.NotPresent.class, ServerResource.IdentityMixin.class } )
+public interface ServerResource<T extends Identity>
+{
+    String identity();
+
+    T get();
+
+    void put( T value );
+
+    void delete();
+
+    RestLink post( RestForm form );
+
+    interface Parameters<T>
+    {
+        @Optional
+        Property<String> id();
+
+        Property<Class<T>> entityType();
+
+        Property<Request> request();
+
+        Property<Response> response();
+
+        Property<Context> context();
+
+        Property<Router> router();
+    }
+
+    abstract class IdentityMixin<T extends Identity>
+        implements ServerResource<T>
+    {
+        @This
+        private Parameters<T> parameters;
+
+        @Service
+        private IdentityManager identityManager;
+
+        @Override
+        public String identity()
+        {
+            return identityManager.generate( parameters.entityType().get(), parameters.id().get() );
+        }
+    }
+
+    abstract class NotPresent
+        implements ServerResource
+    {
+        @Override
+        public Identity get()
+        {
+            throw new NotPresentException();
+        }
+
+        @Override
+        public void put( Identity value )
+        {
+            throw new NotPresentException();
+        }
+
+        @Override
+        public void delete()
+        {
+            throw new NotPresentException();
+        }
+
+        @Override
+        public RestLink post( RestForm form )
+        {
+            throw new NotPresentException();
+        }
+    }
+}