Skip to content

Commit

Permalink
[WICKET-7080] 1) make default events delivery machinery pluggable 2) …
Browse files Browse the repository at this point in the history
…allow to disabled sending events 3) define reflection based machinery to deliver events
  • Loading branch information
reiern70 committed Oct 16, 2023
1 parent 83537c6 commit 6fd7ce9
Show file tree
Hide file tree
Showing 6 changed files with 567 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* 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.wicket.event;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Annotation used to mark a component, a session or application as aware of annotated events
*
* See {@link ReflectionEventDispatcher}
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE_USE, ElementType.TYPE })
public @interface EventAwareObject
{

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* 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.wicket.event;

/**
* To mark an object as able to react to annotated event handler methods
*/
public interface IEventAwareObject {
}
40 changes: 40 additions & 0 deletions wicket-core/src/main/java/org/apache/wicket/event/OnEvent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* 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.wicket.event;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Annotation used to mark a public method (on a component) or on {@link org.apache.wicket.behavior.Behavior}
* as a handler of an Event.
* <p></p>
* See {@link ReflectionEventDispatcher}
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface OnEvent
{

/**
* @return The class of the payload
*/
Class<?> value();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* 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.wicket.event;

import java.lang.reflect.Method;
import org.apache.wicket.Component;
import org.apache.wicket.IEventDispatcher;
import org.apache.wicket.WicketRuntimeException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* IEventDispatcher that uses reflection in order to locate events methods.
*/
public class ReflectionEventDispatcher implements IEventDispatcher {

private static final Logger LOGGER = LoggerFactory.getLogger(ReflectionEventDispatcher.class);

/**
* If set to true then only sinks annotated with @{@link EventAwareObject} will be injected.
* Probably, this should be set to true in most applications and enforce the use of either @{@link EventAwareObject}
* annotation or the marker interface @{@link IEventAwareObject}
*/
private final boolean restrictToEventAware;

public ReflectionEventDispatcher(boolean restrictToEventAware)
{
this.restrictToEventAware = restrictToEventAware;
}

@Override
public final void dispatchEvent(Object sink, IEvent<?> event, Component component)
{
if (restrictToEventAware)
{
Class<?> clazz = sink.getClass();
if (!clazz.isAnnotationPresent(EventAwareObject.class) && !(sink instanceof IEventAwareObject))
{
// object does not receives events
return;
}
}
executeEvent(sink, event);
}

private void executeEvent(Object sink, IEvent<?> event)
{
Class<?> clazz = sink.getClass();
// TODO: allow for private protected methods?
for (Method method : clazz.getMethods())
{
if (isMethodAnEventMethod(method, event))
{
method.setAccessible(true);
try
{
// we try to inject IEvent
method.invoke(sink, event);
}
catch (Exception e)
{
try
{
// we try to inject payload directly
method.invoke(sink, event.getPayload());
}
catch (Exception e1)
{
LOGGER.error("Wrong signature of event method: sink {} and method {}", clazz, method);
throw new WicketRuntimeException("Wrong signature of event method: " + method.getName());
}
}
}
}
}

protected boolean isMethodAnEventMethod(Method method, IEvent<?> event)
{
Class<?> payloadClass = event.getPayload().getClass();
// we look for annotation
if (method.isAnnotationPresent(OnEvent.class))
{
OnEvent onEvent = method.getAnnotation(OnEvent.class);
Class<?> eventClass = onEvent.value();
// and we check payload if of the right type
return eventClass.isAssignableFrom(payloadClass);
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,28 @@
*/
public class FrameworkSettings implements IEventDispatcher
{
/**
* Does the standard delivery of events. Override and do nothing if you want to disable it.
*/
private static class DefaultEventDispatcher implements IEventDispatcher
{
@Override
public void dispatchEvent(Object sink, IEvent<?> event, Component component)
{
// direct delivery
if (component != null && sink instanceof IComponentAwareEventSink)
{
((IComponentAwareEventSink)sink).onEvent(component, event);
}
else if (sink instanceof IEventSink)
{
((IEventSink)sink).onEvent(event);
}
}
}
private IDetachListener detachListener;

private IEventDispatcher defaultEventDispatcher = new DefaultEventDispatcher();
private List<IEventDispatcher> eventDispatchers = null;

/**
Expand All @@ -68,7 +88,7 @@ public FrameworkSettings(final Application application)
* Gets the Wicket version. The Wicket version is in the same format as the version element in
* the pom.xml file (project descriptor). The version is generated by maven in the build/release
* cycle and put in the /META-INF/MANIFEST.MF file located in the root folder of the Wicket jar.
*
* <p></p>
* The version usually follows one of the following formats:
* <ul>
* <li>major.minor[.bug] for stable versions. 1.1, 1.2, 1.2.1 are examples</li>
Expand Down Expand Up @@ -112,7 +132,7 @@ public FrameworkSettings setDetachListener(IDetachListener detachListener)
/**
* Registers a new event dispatcher
*
* @param dispatcher
* @param dispatcher {@link IEventDispatcher}
* @return {@code this} object for chaining
*/
public FrameworkSettings add(IEventDispatcher dispatcher)
Expand All @@ -133,22 +153,14 @@ public FrameworkSettings add(IEventDispatcher dispatcher)
* Dispatches event to registered dispatchers
*
* @see IEventDispatcher#dispatchEvent(Object, IEvent, Component)
*
* @param sink
* @param event
* @param component
*
*/
@Override
public void dispatchEvent(Object sink, IEvent<?> event, Component component)
{
// direct delivery
if (component != null && sink instanceof IComponentAwareEventSink)
if (defaultEventDispatcher != null)
{
((IComponentAwareEventSink)sink).onEvent(component, event);
}
else if (sink instanceof IEventSink)
{
((IEventSink)sink).onEvent(event);
defaultEventDispatcher.dispatchEvent(sink, event, component);
}

// additional dispatchers delivery
Expand All @@ -162,6 +174,19 @@ else if (sink instanceof IEventSink)
}
}

/**
* Allows to set the default events dispatcher
*
* @param defaultEventDispatcher
* IEventDispatcher
* @return {@code this} object for chaining
*/
public FrameworkSettings setDefaultEventDispatcher(IEventDispatcher defaultEventDispatcher)
{
this.defaultEventDispatcher = defaultEventDispatcher;
return this;
}

/**
* Sets the {@link ISerializer} that will be used to convert objects to/from byte arrays
*
Expand Down
Loading

0 comments on commit 6fd7ce9

Please sign in to comment.