blob: 4f8eedc839b23588501206e6b6a49b66966c6153 [file] [log] [blame]
/**
*
* Copyright 2009-2011 Rickard Öberg AB
*
* Licensed 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.qi4j.library.rest.server.restlet;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.qi4j.api.composite.TransientComposite;
import org.qi4j.api.constraint.Constraint;
import org.qi4j.api.constraint.ConstraintDeclaration;
import org.qi4j.api.constraint.Constraints;
import org.qi4j.api.injection.scope.Structure;
import org.qi4j.api.object.NoSuchObjectException;
import org.qi4j.api.structure.Module;
import org.qi4j.library.rest.server.api.ObjectSelection;
import org.qi4j.library.rest.server.api.constraint.InteractionConstraint;
import org.qi4j.library.rest.server.api.constraint.InteractionConstraintDeclaration;
import org.qi4j.library.rest.server.api.constraint.InteractionValidation;
import org.qi4j.library.rest.server.api.constraint.RequiresValid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* JAVADOC
*/
public class InteractionConstraintsService
implements InteractionConstraints
{
@Structure
Module module;
Logger logger = LoggerFactory.getLogger( InteractionConstraintsService.class );
private Map<Method, InteractionConstraintsBinding> methodsConstraints = new ConcurrentHashMap<Method, InteractionConstraintsBinding>();
private Map<Class, InteractionConstraintsBinding> classConstraints = new ConcurrentHashMap<Class, InteractionConstraintsBinding>();
@Override
public boolean isValid( Method method, ObjectSelection objectSelection, Module module )
{
return getConstraints( method, module ).isValid( objectSelection );
}
@Override
public boolean isValid( Class resourceClass, ObjectSelection objectSelection, Module module )
{
return getConstraints( resourceClass, module ).isValid( objectSelection );
}
private InteractionConstraintsBinding getConstraints( Method method, Module module )
{
InteractionConstraintsBinding constraintBindings = methodsConstraints.get( method );
if( constraintBindings == null )
{
constraintBindings = findConstraints( method, module );
methodsConstraints.put( method, constraintBindings );
}
return constraintBindings;
}
private InteractionConstraintsBinding getConstraints( Class aClass, Module module )
{
InteractionConstraintsBinding constraintBindings = classConstraints.get( aClass );
if( constraintBindings == null )
{
constraintBindings = findConstraints( aClass, module );
classConstraints.put( aClass, constraintBindings );
}
return constraintBindings;
}
private InteractionConstraintsBinding findConstraints( Method method, Module module )
{
List<Binding> methodConstraintBindings = new ArrayList<Binding>();
for( Annotation annotation : method.getAnnotations() )
{
if( annotation.annotationType().equals( RequiresValid.class ) )
{
RequiresValid requiresValid = (RequiresValid) annotation;
Class contextClass = method.getDeclaringClass();
if( InteractionValidation.class.isAssignableFrom( contextClass ) )
{
InteractionValidation validation = null;
if( TransientComposite.class.isAssignableFrom( contextClass ) )
{
validation = (InteractionValidation) module.newTransient( contextClass );
}
else
{
validation = (InteractionValidation) module.newObject( contextClass );
}
methodConstraintBindings.add( new RequiresValidBinding( requiresValid, validation ) );
}
}
else if( annotation.annotationType().getAnnotation( ConstraintDeclaration.class ) != null )
{
Constraints constraints = annotation.annotationType().getAnnotation( Constraints.class );
for( Class<? extends Constraint<?, ?>> aClass : constraints.value() )
{
try
{
Constraint<Annotation, Object> constraint = (Constraint<Annotation, Object>) aClass.newInstance();
Class roleClass = (Class) ( (ParameterizedType) aClass.getGenericInterfaces()[ 0 ] ).getActualTypeArguments()[ 1 ];
ConstraintBinding constraintBinding = new ConstraintBinding( constraint, annotation, roleClass );
methodConstraintBindings.add( constraintBinding );
}
catch( InstantiationException e )
{
e.printStackTrace();
}
catch( IllegalAccessException e )
{
e.printStackTrace();
}
}
}
else if( annotation.annotationType().getAnnotation( InteractionConstraintDeclaration.class ) != null )
{
Class<? extends InteractionConstraint> constraintClass = annotation.annotationType()
.getAnnotation( InteractionConstraintDeclaration.class )
.value();
InteractionConstraint<Annotation> constraint = null;
try
{
try
{
constraint = module.newObject( constraintClass );
}
catch( NoSuchObjectException e )
{
constraint = constraintClass.newInstance();
}
}
catch( Exception e )
{
continue; // Skip this constraint
}
InteractionConstraintBinding constraintBinding = new InteractionConstraintBinding( constraint, annotation );
methodConstraintBindings.add( constraintBinding );
}
}
if( methodConstraintBindings.isEmpty() )
{
methodConstraintBindings = null;
}
return new InteractionConstraintsBinding( methodConstraintBindings );
}
private InteractionConstraintsBinding findConstraints( Class aClass, Module module )
{
List<Binding> classConstraintBindings = new ArrayList<Binding>();
for( Annotation annotation : aClass.getAnnotations() )
{
if( annotation.annotationType().getAnnotation( ConstraintDeclaration.class ) != null )
{
Constraints constraints = annotation.annotationType().getAnnotation( Constraints.class );
for( Class<? extends Constraint<?, ?>> constraintClass : constraints.value() )
{
try
{
Constraint<Annotation, Object> constraint = (Constraint<Annotation, Object>) constraintClass.newInstance();
Class roleClass = (Class) ( (ParameterizedType) constraint.getClass()
.getGenericInterfaces()[ 0 ] ).getActualTypeArguments()[ 1 ];
ConstraintBinding constraintBinding = new ConstraintBinding( constraint, annotation, roleClass );
classConstraintBindings.add( constraintBinding );
}
catch( InstantiationException e )
{
e.printStackTrace();
}
catch( IllegalAccessException e )
{
e.printStackTrace();
}
}
}
else if( annotation.annotationType().getAnnotation( InteractionConstraintDeclaration.class ) != null )
{
Class<? extends InteractionConstraint> constraintClass = annotation.annotationType()
.getAnnotation( InteractionConstraintDeclaration.class )
.value();
InteractionConstraint<Annotation> constraint = null;
try
{
try
{
constraint = module.newObject( constraintClass );
}
catch( NoSuchObjectException e )
{
constraint = constraintClass.newInstance();
}
}
catch( Exception e )
{
continue; // Skip this constraint
}
InteractionConstraintBinding constraintBinding = new InteractionConstraintBinding( constraint, annotation );
classConstraintBindings.add( constraintBinding );
}
}
if( classConstraintBindings.isEmpty() )
{
classConstraintBindings = null;
}
return new InteractionConstraintsBinding( classConstraintBindings );
}
interface Binding
{
boolean isValid( ObjectSelection objectSelection );
}
public static class InteractionConstraintsBinding
{
List<Binding> bindings;
public InteractionConstraintsBinding( List<Binding> bindings )
{
this.bindings = bindings;
}
public boolean isValid( ObjectSelection objectSelection )
{
if( bindings != null )
{
for( Binding constraintBinding : bindings )
{
if( !constraintBinding.isValid( objectSelection ) )
{
return false;
}
}
}
return true;
}
}
public class RequiresValidBinding
implements Binding
{
RequiresValid annotation;
private final InteractionValidation validation;
public RequiresValidBinding( RequiresValid annotation, InteractionValidation validation )
{
this.validation = validation;
this.annotation = annotation;
}
@Override
public boolean isValid( ObjectSelection objectSelection )
{
try
{
return validation.isValid( annotation.value() );
}
catch( IllegalArgumentException e )
{
return false;
}
catch( Throwable e )
{
logger.warn( "Could not check validation constraint for '" + annotation.value() + "'", e );
return false;
}
}
}
public class ConstraintBinding
implements Binding
{
Constraint<Annotation, Object> constraint;
Annotation annotation;
Class roleClass;
public ConstraintBinding( Constraint<Annotation, Object> constraint, Annotation annotation, Class roleClass )
{
this.constraint = constraint;
this.annotation = annotation;
this.roleClass = roleClass;
}
@Override
public boolean isValid( ObjectSelection objectSelection )
{
try
{
Object checkedObject = roleClass.equals( ObjectSelection.class ) ? objectSelection : objectSelection.get( roleClass );
return constraint.isValid( annotation, checkedObject );
}
catch( IllegalArgumentException e )
{
return false;
}
catch( Throwable e )
{
logger.warn( "Could not check constraint " + constraint.getClass().getName(), e );
return false;
}
}
}
public class InteractionConstraintBinding
implements Binding
{
InteractionConstraint<Annotation> constraint;
Annotation annotation;
public InteractionConstraintBinding( InteractionConstraint<Annotation> constraint, Annotation annotation )
{
this.constraint = constraint;
this.annotation = annotation;
}
@Override
public boolean isValid( ObjectSelection objectSelection )
{
try
{
return constraint.isValid( annotation, objectSelection );
}
catch( Throwable e )
{
logger.warn( "Could not check constraint " + constraint.getClass().getName(), e );
return false;
}
}
}
}