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