| /* |
| * 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.polygene.library.restlet; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.function.Consumer; |
| import org.apache.polygene.api.common.Optional; |
| import org.apache.polygene.api.entity.EntityComposite; |
| import org.apache.polygene.api.identity.HasIdentity; |
| import org.apache.polygene.api.injection.scope.Service; |
| import org.apache.polygene.api.injection.scope.Structure; |
| import org.apache.polygene.api.injection.scope.Uses; |
| import org.apache.polygene.api.structure.Module; |
| import org.apache.polygene.api.unitofwork.NoSuchEntityException; |
| import org.apache.polygene.api.unitofwork.UnitOfWork; |
| import org.apache.polygene.api.unitofwork.UnitOfWorkFactory; |
| import org.apache.polygene.api.usecase.UsecaseBuilder; |
| import org.apache.polygene.api.value.ValueBuilder; |
| import org.apache.polygene.api.value.ValueBuilderFactory; |
| import org.apache.polygene.library.restlet.metainfo.UserIdentity; |
| import org.apache.polygene.library.restlet.repository.RepositoryLocator; |
| import org.apache.polygene.library.restlet.resource.DefaultResourceFactoryImpl; |
| import org.apache.polygene.library.restlet.resource.NotPresentException; |
| import org.apache.polygene.library.restlet.resource.ResourceFactory; |
| import org.apache.polygene.library.restlet.resource.ServerResource; |
| import org.apache.polygene.library.restlet.serialization.PolygeneConverter; |
| import org.apache.polygene.spi.PolygeneSPI; |
| import org.restlet.Context; |
| import org.restlet.Request; |
| import org.restlet.Response; |
| import org.restlet.Restlet; |
| import org.restlet.data.Form; |
| import org.restlet.data.Method; |
| import org.restlet.data.Parameter; |
| import org.restlet.data.Status; |
| import org.restlet.representation.Representation; |
| import org.restlet.representation.Variant; |
| import org.restlet.routing.Router; |
| import org.restlet.security.User; |
| |
| public class PolygeneEntityRestlet<T extends HasIdentity> extends Restlet |
| { |
| /** |
| * Creates a new PolygeneEntityRestlet instance for the given resource and entity classes. |
| * <p> |
| * This utility method should be used in your org.restlet.Application to create routes. |
| * |
| * @param <K> Parameterized type of the resource |
| * @param <T> Parameterized type of the entity |
| * @param module Module to use for object instanciation |
| * @param router Restlet Router |
| * @param resourceClass Resource class |
| * @param entityClass Entity class |
| * |
| * @return The PolygeneEntityRestlet instance |
| */ |
| public static <K extends HasIdentity, T extends ServerResource<K>> Restlet newInstance( |
| Module module, Router router, Class<T> resourceClass, Class<K> entityClass |
| ) |
| { |
| @SuppressWarnings( "unchecked" ) |
| ResourceFactory<K, T> factory = module.newObject( DefaultResourceFactoryImpl.class, resourceClass, router ); |
| return module.newObject( PolygeneEntityRestlet.class, factory, router, entityClass, new PolygeneConverter( module ) ); |
| } |
| |
| @Structure |
| private ValueBuilderFactory vbf; |
| |
| @Structure |
| private UnitOfWorkFactory uowf; |
| |
| @Uses |
| private ResourceFactory resourceFactory; |
| |
| @Uses |
| private Router router; |
| |
| @Uses |
| @Optional |
| private Class<T> identityType; |
| |
| @Structure |
| private PolygeneSPI spi; |
| |
| @Uses |
| private PolygeneConverter converter; |
| |
| @Service |
| private RepositoryLocator locator; |
| |
| @Override |
| public void handle( Request request, Response response ) |
| { |
| try |
| { |
| super.handle( request, response ); |
| Method method = request.getMethod(); |
| if( method.equals( Method.GET ) ) |
| { |
| get( request, response ); |
| } |
| if( method.equals( Method.DELETE ) ) |
| { |
| delete( request, response ); |
| } |
| if( method.equals( Method.POST ) ) |
| { |
| post( request, response ); |
| } |
| if( method.equals( Method.PUT ) ) |
| { |
| put( request, response ); |
| } |
| } |
| catch( RuntimeException e ) |
| { |
| e.printStackTrace(); |
| throw e; |
| } |
| } |
| |
| private void get( Request request, Response response ) |
| { |
| execute( request, response, resource -> { |
| try |
| { |
| T result = resource.get(); |
| if( result != null ) |
| { |
| if( result instanceof EntityComposite ) |
| { |
| result = locator.find( identityType ).toValue( result ); |
| } |
| Representation representation = converter.toRepresentation( result, new Variant(), null ); |
| response.setEntity( representation ); |
| response.setStatus( Status.SUCCESS_OK ); |
| } |
| else |
| { |
| response.setStatus( Status.CLIENT_ERROR_NOT_FOUND ); |
| } |
| } |
| catch( NoSuchEntityException e ) |
| { |
| response.setStatus( Status.CLIENT_ERROR_NOT_FOUND, e, "Entity not found." ); |
| } |
| } |
| ); |
| } |
| |
| private void put( Request request, Response response ) |
| { |
| execute( request, response, resource -> { |
| |
| T value = convertToObject( identityType, request ); |
| resource.put( value ); |
| response.setStatus( Status.SUCCESS_OK ); |
| } ); |
| } |
| |
| private void delete( Request request, Response response ) |
| { |
| execute( request, response, resource -> { |
| resource.delete(); |
| response.setStatus( Status.SUCCESS_NO_CONTENT ); |
| } ); |
| } |
| |
| private void post( final Request request, final Response response ) |
| { |
| execute( request, response, resource -> { |
| RestForm form = createRestForm( request ); |
| RestLink link = resource.post( form ); |
| response.setLocationRef( link.path().get() ); |
| response.setStatus( Status.REDIRECTION_SEE_OTHER ); |
| } ); |
| } |
| |
| private RestForm createRestForm( final Request request ) |
| { |
| //noinspection MismatchedQueryAndUpdateOfCollection |
| Form form = new Form( request.getEntity() ); |
| ValueBuilder<RestForm> builder = vbf.newValueBuilderWithState( |
| RestForm.class, |
| descriptor -> { |
| if( descriptor.qualifiedName().name().equals( "fields" ) ) |
| { |
| List<FormField> result = new ArrayList<>(); |
| for( Parameter param : form ) |
| { |
| String name = param.getName(); |
| String value = param.getValue(); |
| ValueBuilder<FormField> fieldBuilder = vbf.newValueBuilder( FormField.class ); |
| FormField prototype = fieldBuilder.prototype(); |
| prototype.name().set( name ); |
| prototype.value().set( value ); |
| prototype.type().set( FormField.TEXT ); |
| result.add( fieldBuilder.newInstance() ); |
| } |
| return result; |
| } |
| return null; |
| }, |
| descriptor -> null, |
| descriptor -> null, |
| descriptor -> null |
| ); |
| return builder.newInstance(); |
| } |
| |
| private void execute( Request request, Response response, Consumer<ServerResource<T>> closure ) |
| { |
| UnitOfWork uow = null; |
| try |
| { |
| uow = createUnitOfWork( request ); |
| ServerResource<T> resource = createResource( request, response, getContext() ); |
| closure.accept( resource ); |
| uow.complete(); |
| } |
| catch( UnsupportedOperationException e ) |
| { |
| e.printStackTrace(); |
| response.setStatus( Status.CLIENT_ERROR_BAD_REQUEST, e, e.getMessage() ); |
| } |
| catch( ConversionException e ) |
| { |
| e.printStackTrace(); |
| response.setStatus( Status.CLIENT_ERROR_BAD_REQUEST, e.getMessage() ); |
| } |
| catch( NotPresentException e ) |
| { |
| e.printStackTrace(); |
| response.setStatus( Status.CLIENT_ERROR_NOT_FOUND, e.getMessage() ); |
| } |
| catch( Throwable e ) |
| { |
| e.printStackTrace(); |
| response.setStatus( Status.CLIENT_ERROR_BAD_REQUEST, e.getMessage() ); |
| } |
| finally |
| { |
| if( uow != null && uow.isOpen() ) |
| { |
| uow.discard(); |
| } |
| } |
| } |
| |
| private ServerResource<T> createResource( Request request, Response response, Context context ) |
| { |
| @SuppressWarnings( "unchecked" ) |
| ServerResource<T> serverResource = resourceFactory.create( identityType, request, response, context ); |
| return serverResource; |
| } |
| |
| private UnitOfWork createUnitOfWork( Request request ) |
| { |
| UsecaseBuilder usecaseBuilder = UsecaseBuilder.buildUsecase( request.getResourceRef().getIdentifier( true ) ); |
| User user = request.getClientInfo().getUser(); |
| if( user != null ) |
| { |
| UserIdentity userIdentity = new UserIdentity( user.getIdentifier(), |
| user.getName(), |
| user.getEmail(), |
| user.getFirstName(), |
| user.getLastName() |
| ); |
| usecaseBuilder.withMetaInfo( userIdentity ); |
| } |
| return uowf.newUnitOfWork( usecaseBuilder.newUsecase() ); |
| } |
| |
| private <K> K convertToObject( Class<K> type, Request request ) |
| { |
| try |
| { |
| return converter.toObject( request.getEntity(), type, null ); |
| } |
| catch( IOException e ) |
| { |
| throw new ConversionException( request.getEntityAsText() ); |
| } |
| } |
| |
| @Override |
| public String toString() |
| { |
| return "PolygeneRestlet[" + ( identityType == null ? "<null>" : identityType.getSimpleName() ) + ", " + resourceFactory + "]"; |
| } |
| } |