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 17, 2023
1 parent 83537c6 commit 3f2fb29
Show file tree
Hide file tree
Showing 10 changed files with 912 additions and 16 deletions.
8 changes: 6 additions & 2 deletions wicket-core/src/main/java/org/apache/wicket/Component.java
Original file line number Diff line number Diff line change
Expand Up @@ -4445,8 +4445,12 @@ public void onEvent(IEvent<?> event)
@Override
public final <T> void send(IEventSink sink, Broadcast type, T payload)
{
new ComponentEventSender(this, getApplication().getFrameworkSettings()).send(sink, type,
payload);
// if there are no event dispatchers then don't even try to send event
if (getApplication().getFrameworkSettings().hasAnyAnyEventDispatchers())
{
new ComponentEventSender(this, getApplication().getFrameworkSettings()).send(sink, type,
payload);
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public void stop()
stop = true;
}

boolean isStop()
public boolean isStop()
{
return stop;
}
Expand Down
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
{

}
5 changes: 5 additions & 0 deletions wicket-core/src/main/java/org/apache/wicket/event/IEvent.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ public interface IEvent<T>
*/
void stop();

/**
* @return true iff event has been stopped.
*/
boolean isStop();

/**
* Stops the broadcast of this event any deeper into the hierarchy of the current sink
*/
Expand Down
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 (component, session, app) as containing annotated event handler methods.
*/
public interface IEventAwareObject {
}
42 changes: 42 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,42 @@
/*
* 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.Repeatable;
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)
@Repeatable(OnEvents.class)
public @interface OnEvent
{

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

}
37 changes: 37 additions & 0 deletions wicket-core/src/main/java/org/apache/wicket/event/OnEvents.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* 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;

/**
* To allow adding more than one @{@link OnEvent} annotation
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface OnEvents
{

/**
* @return The class of the payload
*/
OnEvent[] value();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
* 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 static java.util.Arrays.asList;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
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 boolean restrictToEventAware = true;

public ReflectionEventDispatcher()
{
}

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();
for (Method method : getAllMethods(clazz))
{
if (event.isStop())
{
return;
}
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());
}
}
}
}
}

private Iterable<Method> getAllMethods(final Class<?> clazz)
{
return new HashSet<>(getDeclared(clazz));
}

protected Set<Method> getDeclared(Class<?> clazz) {
if (clazz == Object.class || clazz == null)
{
return Collections.emptySet();
}
Set<Method> allMethods = new HashSet<>();
allMethods.addAll(asList(clazz.getDeclaredMethods()));
allMethods.addAll(getDeclared(clazz.getSuperclass()));
return allMethods;
}

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;
}
}
Loading

0 comments on commit 3f2fb29

Please sign in to comment.