|
リスト1.DomainValidator
package jp.co.exa_corp.validator.hibernate;
import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException;
import javax.faces.FacesException; import javax.faces.context.FacesContext; import javax.faces.component.UIComponent; import javax.faces.application.FacesMessage; import javax.faces.validator.Validator; import javax.faces.validator.ValidatorException;
import jp.co.exa_corp.validator.ValidationException;
import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * * DomainValidator : Path through to ValdatotionInterceptor * * @author Masato Tuschiya 2008/7/15 */
public class DomainValidator implements Validator { private static Log log = LogFactory.getLog( DomainValidator.class ); public void validate( FacesContext context, UIComponent component, Object value ) throws ValidatorException { String el = component.getValueExpression("value").getExpressionString();
// Get target object of expression language in the input tag // ex. <h:inputText size="2" value="#{select.number}" required="true"> // target = select Object target = context.getApplication().getExpressionFactory(). createValueExpression( context.getELContext(), getTargetObjectExpression( el ), Object.class ). getValue( context.getELContext());
log.trace("validate: ("+target+")."+getTargetMethodName(el)+"("+ value.getClass().getName()+" "+value+")"); Method method = null; String methodName = getTargetMethodName( el );
try { // Invoke the target method // ex. <h:inputText size="2" value="#{select.number}" required="true"> // invoke select.setNumber( value ); method = target.getClass().getMethod( methodName, new Class[]{ value.getClass()});
} catch( NoSuchMethodException e ) { Class type = Void.TYPE; if( value instanceof Short ) { type = Short.TYPE; } else if( value instanceof Long ) { type = Long.TYPE; } else if( value instanceof Integer ) { type = Integer.TYPE; } else if( value instanceof Double ) { type = Double.TYPE; } else if( value instanceof Float ) { type = Float.TYPE; } else if( value instanceof Byte ) { type = Byte.TYPE; } else if( value instanceof Character ) { type = Character.TYPE; } else if( value instanceof Boolean ) { type = Boolean.TYPE; } else { throw new NoSuchMethodError("method: "+ methodName +"("+ value.getClass().getName() +") not found"); } try { method = target.getClass().getMethod( methodName, new Class[]{ type }); } catch( NoSuchMethodException n ) { throw new NoSuchMethodError("method: "+ methodName +"("+ value.getClass().getName() +") not found"); } } try { method.invoke( target, new Object[]{ value }); } catch( InvocationTargetException t ) { throw new ValidatorException( new FacesMessage( t.getTargetException().getMessage())); } catch( Exception e ) { throw new IllegalAccessError( e.getMessage()); } } private static String getTargetMethodExpression( String s ) { // #{a.b} ==> #{a.setB} s = s.substring( 2, s.length() - 1 ); int n = s.lastIndexOf("."); // ToDo for n < 0 return "#{"+ s.substring( 0, n ) +".set"+ capitalize( s.substring( n+1, s.length())) +"}"; } private static String getTargetObjectExpression( String s ) { // #{a.b.c} ==> #{a.b} s = s.substring( 2, s.length() - 1 ); int n = s.lastIndexOf("."); return "#{" + ( n < 0 ? s : s.substring( 0, n )) + "}"; } private static String getTargetMethodName( String s ) { // #{a.b} ==> setB s = s.substring( 2, s.length() - 1 ); int n = s.lastIndexOf("."); // ToDo for n < 0 return "set"+ capitalize( s.substring( n+1, s.length())); } private String getTargetPropertyName( final String s ) { // #{a.b} ==> b int n = s.lastIndexOf("."); // ToDo for n < 0 return s.substring( n+1, s.length() - 1 ); } private static String capitalize( final String s ) { // abc ==> Abc return s.substring(0,1).toUpperCase() + s.substring(1,s.length()); } }
リスト2.ValidationInterceptor
package jp.co.exa_corp.validator.hibernate.interceptor;
import java.util.Map; import java.lang.reflect.Method; import java.lang.ref.WeakReference; import java.lang.annotation.Annotation;
import jp.co.exa_corp.validator.Validation; // exa annotation import jp.co.exa_corp.validator.ValidationException;
import org.aspectj.lang.Signature; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.reflect.MethodSignature; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut;
import org.hibernate.validator.ClassValidator; import org.hibernate.validator.InvalidValue;
import jp.co.exa_corp.validator.Validation; // exa annotation import jp.co.exa_corp.validator.ValidationException;
import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * <BR>ValidationInterceptor : Validator Using Hibernate Validator</BR> * * @author Masato Tuschiya 2008/8/07 * @see http://weblogs.java.net/blog/urubatan/archive/2006/09/aspectj_hiberna.html * */ @Aspect public class ValidationInterceptor implements java.io.Serializable { private static Log log = LogFactory.getLog( ValidationInterceptor.class );
// Our use own annotation:jp.co.exa_corp.validator.Validation to detect pointcut @Pointcut("execution( @jp.co.exa_corp.validator.Validation public * *.set*(*))") // only one argument public void needValidation(){}
@Around("needValidation()") @SuppressWarnings("unchecked") public Object validate( final ProceedingJoinPoint thisJoinPoint ) throws Throwable { final Object[] args = thisJoinPoint.getArgs(); final String method = thisJoinPoint.getSignature().getName(); log.debug("validate:"+ thisJoinPoint.getSignature() +" "+ args[0].getClass().getName() +"="+ args[0]); if( ValidationHelper.performValidation() && args != null && args.length == 1 && isSetter( method )) { final Class clazz = thisJoinPoint.getTarget().getClass(); Validation validation = (Validation)((MethodSignature)thisJoinPoint.getSignature()). getMethod().getAnnotation( Validation.class ); if( validation == null ) { throw new NoSuchMethodError("method: "+ thisJoinPoint.getSignature() +" has not @Validation"); }
final ClassValidator validator = getClassValidator( clazz ); // We don't update value but only validate. final InvalidValue[] invalidmessages=validator.getPotentialInvalidValues(getPropertyName( method ),args[0]); if ((invalidmessages != null) && (invalidmessages.length > 0)) { StringBuilder buff = new StringBuilder(); for( InvalidValue iv : invalidmessages ) { buff.append( "¥"" ); // jp.co.exa_corp.validator.Validation.expression = attribute name buff.append( validation.expression()); buff.append( "¥"" ); buff.append( iv.getMessage()); buff.append( "¥n" ); } buff.setLength( buff.length() - 1 ); log.debug("validate:" + buff.toString()); throw new ValidationException( buff.toString()); } } return thisJoinPoint.proceed( args ); } private boolean isSetter( String method ) { return method.startsWith( "set" ); } private String getPropertyName( final String method ) { return method.substring( 3, 4 ).toLowerCase() + method.substring( 4 ); } // Cache for Hibernate Validator private static final Map< Class, ClassValidator> validators; static { validators = new SoftHashMap< Class, ClassValidator>(); } @SuppressWarnings("unchecked") private static ClassValidator getClassValidator( Class clazz ) { ClassValidator validator = null; synchronized( validators ) { validator = validators.get( clazz ); if( validator == null ) { validator = new ClassValidator( clazz ); validators.put( clazz, validator ); } } return validator; } }
リスト3.Validation
package jp.co.exa_corp.validator;import java.lang.annotation.Target; import java.lang.annotation.Retention; import java.lang.annotation.Inherited; import java.lang.annotation.Documented; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Target( METHOD ) @Retention( RUNTIME ) @Documented @Inherited public @interface Validation { public String expression(); }
リスト4.ValidationPhaseListener
package jp.co.exa_corp.validator.hibernate.interceptor;
import javax.faces.event.PhaseId; import javax.faces.event.PhaseEvent; import javax.faces.event.PhaseListener;
import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * * ValidationPhaseListener * * @author Masato Tuschiya 2008/7/15 */ public class ValidationPhaseListener implements PhaseListener { private static Log log = LogFactory.getLog( ValidationPhaseListener.class ); public void beforePhase(javax.faces.event.PhaseEvent event) { log.trace("beforePhase:"+ event.getPhaseId()); ValidationHelper.setPhaseId( event ); } public void afterPhase(javax.faces.event.PhaseEvent event) { log.trace("afterPhase:"+ event.getPhaseId()); } public PhaseId getPhaseId() { return PhaseId.ANY_PHASE; } }
リスト5.ValidationHelper
package jp.co.exa_corp.validator.hibernate.interceptor;
import java.util.List; import java.util.ArrayList; import javax.faces.context.FacesContext; import javax.faces.event.PhaseId; import javax.faces.event.PhaseEvent; import javax.faces.event.PhaseListener;
import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * * ValidationHelper : Helper for ValidationInterceptor * * @author Masato Tuschiya 2008/8/07 */ public class ValidationHelper { private static final String PHASE_ID_KEY = "ValidatorHelper_PhaseId_Key"; private static Log log = LogFactory.getLog( ValidationHelper.class ); public static synchronized void setPhaseId( PhaseEvent event ) { event.getFacesContext().getExternalContext().getRequestMap().put( PHASE_ID_KEY, event.getPhaseId()); } public static synchronized PhaseId getPhaseId( FacesContext context ) { return (PhaseId)context.getExternalContext().getRequestMap().get( PHASE_ID_KEY ); } public static synchronized boolean performValidation() throws IllegalStateException { FacesContext context = FacesContext.getCurrentInstance(); if( context == null ) { return true; // Out of JSF ( ex. Unit Test ) } PhaseId phaseId = getPhaseId( context ); if( phaseId == null ) { throw new IllegalStateException("Cannot get PhaseId. Check that ValidationPhaseListner is registered to JSF"); } return phaseId.equals( PhaseId.PROCESS_VALIDATIONS ); // Validate only on PROCESS_VALIDATIONS Phase } }
リスト 6.SoftHashMap
package jp.co.exa_corp.validator.hibernate.interceptor;
import java.util.*; import java.lang.ref.*;
import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory;
/** SoftHashMap
@author Dr. Heinz Kabutz @see http://archive.devx.com/java/free/articles/Kabutz01/Kabutz01-1.asp Modified for Java 5 Generic by Masato Tsuchiya */ public class SoftHashMap<K,V> extends AbstractMap<K,V> { private static Log log = LogFactory.getLog( SoftHashMap.class );
/** The internal HashMap that will hold the SoftReference. */ private final Map<K,SoftValue<K,V>> hash = new HashMap<K,SoftValue<K,V>>(); /** The FIFO list of hard references, order of last access. */ private final LinkedList<V> hardCache = new LinkedList<V>(); /** Reference queue for cleared SoftReference objects. */ private final ReferenceQueue<V> queue = new ReferenceQueue<V>(); /** The number of "hard" references to hold internally. */ private int HARD_SIZE;
/** We define our own subclass of SoftReference which contains * not only the value but also the key to make it easier to find * the entry in the HashMap after it's been garbage collected. */ private static class SoftValue<K,V> extends SoftReference<V> { private final K key; // always make data member final /** Did you know that an outer class can access private data * members and methods of an inner class? I didn't know that! * I thought it was only the inner class who could access the * outer class's private information. An outer class can also * access private members of an inner class inside its innerclass. */ private SoftValue( V value, K key, ReferenceQueue<V> queue ) { super( value, queue ); this.key = key; } }
public SoftHashMap() { this(100); } public SoftHashMap(int hardSize) { HARD_SIZE = hardSize; }
@Override public V get( Object key ) { V result = null; // We get the SoftReference represented by that key SoftReference<V> soft_ref = hash.get(key); if (soft_ref != null) { /** From the SoftReference we get the value, which can be * null if it was not in the map, or it was removed in * the processQueue() method defined below */ result = soft_ref.get(); if ( result == null) { /** * If the value has been garbage collected, remove the * entry from the HashMap. */ log.trace( key +" is garbage collected"); hash.remove(key); } else { /** * We now add this object to the beginning of the hard * reference queue. One reference can occur more than * once, because lookups of the FIFO queue are slow, so * we don't want to search through it each time to remove * duplicates. */ log.trace( "cache hit: "+ key ); hardCache.addFirst(result); if (hardCache.size() > HARD_SIZE) { // Remove the last entry if list longer than HARD_SIZE hardCache.removeLast(); } } } else { log.trace( "cache miss hit: "+ key ); } return result; }
/** Here we go through the ReferenceQueue and remove garbage * collected SoftValue objects from the HashMap by looking them * up using the SoftValue.key data member. */ @SuppressWarnings("unchecked") private void processQueue() { SoftValue<K,V> sv; while ((sv = (SoftValue<K,V>)queue.poll()) != null) { hash.remove(sv.key); // we can access private data! } } /** Here we put the key, value pair into the HashMap using a SoftValue object. */ @Override public V put( K key, V value) { SoftValue<K,V> r; processQueue(); // throw out garbage collected values first return ( r = hash.put( key, new SoftValue<K,V>(value, key, queue))) != null ? r.get():null; } @Override public V remove( Object key ) { SoftValue<K,V> r; processQueue(); // throw out garbage collected values first return ( r = hash.remove( key )) !=null ? r.get():null; } @Override public void clear() { hardCache.clear(); processQueue(); // throw out garbage collected values hash.clear(); } @Override public int size() { processQueue(); // throw out garbage collected values first return hash.size(); } @Override public Set<Map.Entry<K,V>> entrySet() { // no, no, you may NOT do that!!! GRRR throw new UnsupportedOperationException(); } } |