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();
+ }
+ }
+}