-
1. Re: Configurable Interceptor
schlegel Apr 18, 2008 5:28 PM (in response to wrzep)You could use Annotations.... I spent some time to develop a tracking interceptor - not relay tested yet!
First you define a marker Annotation for class level:
@Target({TYPE}) @Retention(RUNTIME) @Interceptors(TrackingInterceptor.class) @Documented @Inherited public @interface Trackable {}
Second for method level:
@Target({METHOD}) @Retention(RUNTIME) @Documented @Inherited public @interface Tracked { /** * @return special RequestTrack-Parameter or RequestTrack-Parameters to track */ TrackedValue[] values() default {}; }
@Target({METHOD}) @Retention(RUNTIME) @Documented @Inherited public @interface TrackedValue { String parameter() default ""; String expr() default ""; }
Your interceptor should look something similar to:
@Interceptor(stateless = true, around = AsynchronousInterceptor.class) public class TrackingInterceptor extends AbstractInterceptor implements Serializable { private static final long serialVersionUID = 4284998401280292997L; @AroundInvoke public Object aroundInvoke(InvocationContext invocationContext) throws Exception { Method method = invocationContext.getMethod(); if (method.isAnnotationPresent(Tracked.class)) { EntityManager em = (EntityManager) Component.getInstance("entityManager"); // TODO: eigener trackingEM FacesContext fc = FacesContext.getCurrentInstance(); ExternalContext ec = fc.getExternalContext(); RequestTrack rt = new RequestTrack(); rt.setSessionId(((Cookie)FacesContext.getCurrentInstance().getExternalContext().getRequestCookieMap().get("JSESSIONID")).getValue()); rt.setRequestTime(new DateTime().toDate()); rt.setUserId(((SysUser)Contexts.getSessionContext().get("loggedInUser")).getId()); rt.setViewId(fc.getViewRoot().getViewId()); rt.setComponentName(getComponent().getName()); rt.setMethodName(method.getName()); Tracked trackedAnnotaion = method.getAnnotation(Tracked.class); String result = ""; for (TrackedValue trv : trackedAnnotaion.values()) { Object eval = Expressions.instance().createValueExpression(trv.expr()).getValue(); if (eval != null) { String param = trv.parameter(); String val = eval.toString(); if (!"".equals(param) && !"".equals(val)) { rt.addTrackedParameter(trv.parameter(), eval.toString()); } } } em.persist(rt); em.flush(); } return invocationContext.proceed(); } }
Now you can use it like this:
@Stateful @Trackable @Name("municipalitySearch") @Scope(ScopeType.CONVERSATION) public class MunicipalitySearchAction implements MunicipalitySearach, Serializable { .... @Tracked(values = { @TrackedValue(parameter = "name", expr = "#{municipalitySearch.municipalitySearchForm.name}"), @TrackedValue(parameter = "id", expr = "#{municipalitySearch.municipalitySearchForm.statisticNumber}") }) public void edit(Municipality municipality) {
I used this approach with the following Tracking Entity-Bean:
@Entity(name = "HTTPRequest") public class RequestTrack { @Id @GeneratedValue @Column(name = "Id") private Long id; @Column(name = "SessionId") @org.hibernate.validator.Length(max = 150) @org.hibernate.validator.NotNull private String sessionId; @Column(name = "UserId") private Long userId; @Column(name = "RequestTime") @Temporal(TemporalType.TIMESTAMP) private Date requestTime; @Column(name = "ComponentName") @org.hibernate.validator.Length(max = 150) private String componentName; @Column(name = "MethodName") @org.hibernate.validator.Length(max = 150) private String methodName; @Column(name = "ViewId") @org.hibernate.validator.Length(max = 150) private String viewId; @org.hibernate.annotations.CollectionOfElements @JoinTable(name = "HTTPRequest_Parameter", joinColumns = @JoinColumn(name = "HTTPRequest_Id") ) @org.hibernate.annotations.MapKey( columns = @Column(name = "Parameter", nullable = false, length = 150) ) @Column(name = "Value", nullable = false, length = 250) private Map<String, String> trackedParameters = new HashMap<String, String>();
-
2. Re: Configurable Interceptor
wrzep Apr 21, 2008 11:32 AM (in response to wrzep)Hi Hans,
Thanks for your reply. Yeah, using annotation parameters is an option. I don't know why I didn't think about it ;)
This of course only allows configuration in the java code, but I hope that sooner or later interceptors will be configurable in the components.xml.
-Pawel
-
3. Re: Configurable Interceptor
cpopetz Apr 21, 2008 2:32 PM (in response to wrzep)I created a
delegating interceptor
which just takes the name of a component you wish to intercept with, which must contain a method annotated @AroundInvoke like a normal interceptor. Code follows. You'd use it like this:@InterceptWith(value="myInterceptingComponent") public class ClassIWantToIntercept() { ... }
and
@Name("myInterceptingComponent") public class MyClass { @AroundInvoke public Object intercept(InvocationContext invocation, Component component) throws Throwable { ... }
Note that all methods are intercepted by default, but that you can also apply @InterceptWith(allMethods=false) and then also apply @InterceptWith to each method you want to intercept.
In InterceptWith.java:
import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; import org.jboss.seam.annotations.intercept.Interceptors; /** * Annotating a class with @InterceptWith will cause the component specified with the annotation * to be intercept the invocation of any method in the target class * The component specified must have a method annotated @AroundInvoke. * If @InterceptWith(allMethods=false) is specified, then only individual methods annotated * with @InterceptWith will be logged. Conversely, if allMethods=true, you can turn off * logging for an individual method with @InterceptWith(ignore=true). */ @Target({ElementType.METHOD,ElementType.TYPE}) @Retention(RUNTIME) @Interceptors(DelegatingInterceptor.class) public @interface InterceptWith{ /** * The name of a component that contains a method annotetd with @AroundInvoke */ String value() default ""; /** * If "cacheable" is true, the implementation will assume that the component can be cached once * instantiated, i.e. it is an Application scoped component. Otherwise the component will be looked up * upon each invocation, which can be costly, so be careful. */ boolean cacheable() default true; /** * If allMethods is turned off, only methods that are also annotated with @InterceptWith will be logged */ boolean allMethods() default true; /** * If ignore is turned on, logging for this method is ellided, even if allMethods is true for the class. */ boolean ignore() default false; }
import java.io.Serializable; import java.lang.reflect.Method; import java.util.HashSet; import java.util.Set; import javax.annotation.PostConstruct; import org.jboss.seam.Component; import org.jboss.seam.annotations.intercept.AroundInvoke; import org.jboss.seam.annotations.intercept.Interceptor; import org.jboss.seam.intercept.InvocationContext; /** * Any component annotated with @InterceptWith InterceptWith will have this interceptor installed for it. * We just delegate to the @AroundInvoke method on the component which is identified by the InterceptWith value annotation parameter * @author cpopetz * */ @Interceptor public class DelegatingInterceptor implements Serializable { private transient InterceptWith classAnnotation; private transient Component component; private transient Object cachedDelegate; private transient Set<Method> filteredMethods = new HashSet<Method>(); private transient Method delegateInterceptMethod; public void setAnnotation(InterceptWith annotation) { this.classAnnotation = annotation; } public void setComponent(Component component) { this.component = component; } /** * Hook into postConstruct to set up the list of methods we should actually intercept, * saving them in the filteredMethods set */ @PostConstruct public void postConstruct(InvocationContext context) { boolean all = classAnnotation.allMethods(); for (Class clazz = component.getBeanClass(); clazz!=Object.class; clazz = clazz.getSuperclass() ) { for (Method m : clazz.getDeclaredMethods()) { InterceptWith methodLevel = m.getAnnotation(InterceptWith.class); if ((all && (methodLevel == null || !methodLevel.ignore())) || (!all && (methodLevel != null && !methodLevel.ignore()))) filteredMethods.add(m); } } } @AroundInvoke public Object intercept(InvocationContext invocation) throws Exception { if (!filteredMethods.contains(invocation.getMethod())) return invocation.proceed(); Object delegate = cachedDelegate; if (delegate == null) { delegate = Component.getInstance(classAnnotation.value()); if (classAnnotation.cacheable()) cachedDelegate = delegate; /* * Scan the delegate and find its @AroundInvoke method */ if (delegateInterceptMethod == null) { for (Method m : Component.forName(classAnnotation.value()).getBeanClass().getDeclaredMethods()) { if (m.isAnnotationPresent(AroundInvoke.class)) if (delegateInterceptMethod != null) throw new RuntimeException("Can't annotate two methods in " + delegate.getClass().getName() + " with @AroundInvoke"); else delegateInterceptMethod = m; } if (delegateInterceptMethod == null) throw new RuntimeException("Must annotate a method in " + delegate.getClass().getName() + " with @AroundInvoke"); } } if (delegate != null) return delegateInterceptMethod.invoke(delegate, invocation, component); else { System.err.println("No delegate component for InterceptWith(\"" + classAnnotation.value() + "\") on invocation of " + invocation.getMethod() + " on " + invocation.getTarget()); return invocation.proceed(); } } }
-
4. Re: Configurable Interceptor
cpopetz Apr 21, 2008 2:34 PM (in response to wrzep)I failed to mention that this of course means you can define your interceptors in components.xml, and annotate the methods you wish to intercept with @InterceptWith.
-