Added support for EL based event filter conditions
This commit is contained in:
parent
de9515ed7d
commit
e170301c2c
539
pom.xml
539
pom.xml
|
@ -1,261 +1,278 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
|
||||
<parent>
|
||||
<groupId>org.sonatype.oss</groupId>
|
||||
<artifactId>oss-parent</artifactId>
|
||||
<version>7</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>net.engio</groupId>
|
||||
<artifactId>mbassador</artifactId>
|
||||
<version>1.1.11-SNAPSHOT</version>
|
||||
<packaging>bundle</packaging>
|
||||
<name>mbassador</name>
|
||||
<description>
|
||||
Mbassador is a fast and flexible message bus system following the publish subscribe pattern.
|
||||
It is designed for ease of use and aims to be feature rich and extensible
|
||||
while preserving resource efficiency and performance.
|
||||
|
||||
It features:
|
||||
declarative handler definition via annotations,
|
||||
sync and/or async message delivery,
|
||||
weak-references,
|
||||
message filtering,
|
||||
ordering of message handlers etc.
|
||||
</description>
|
||||
|
||||
<url>https://github.com/bennidi/mbassador</url>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>MIT license</name>
|
||||
<url>http://www.opensource.org/licenses/mit-license.php</url>
|
||||
</license>
|
||||
</licenses>
|
||||
<scm>
|
||||
<url>git@github.com:bennidi/mbassador.git</url>
|
||||
<connection>scm:git:git@github.com:bennidi/mbassador.git</connection>
|
||||
<tag>mbassador-1.1.4</tag>
|
||||
<developerConnection>scm:git:git@github.com:bennidi/mbassador.git</developerConnection>
|
||||
</scm>
|
||||
<developers>
|
||||
|
||||
<developer>
|
||||
<id>bennidi</id>
|
||||
<name>Benjamin Diedrichsen</name>
|
||||
<timezone>+1</timezone>
|
||||
<email>b.diedrichsen@googlemail.com</email>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<properties>
|
||||
<nazgul-codestyle.version>2.0.1</nazgul-codestyle.version>
|
||||
<jdk.version>1.6</jdk.version>
|
||||
<pmd.plugin.version>3.0.1</pmd.plugin.version>
|
||||
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.build.java.version>1.6</project.build.java.version>
|
||||
<github.url>file://${project.basedir}/mvn-local-repo</github.url>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.10</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>1.7.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
<version>1.7.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<!-- Local repository (for testing)
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>mbassador-github-repo</id>
|
||||
<url>${github.url}</url>
|
||||
</repository>
|
||||
</distributionManagement>
|
||||
-->
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-pmd-plugin</artifactId>
|
||||
<version>${pmd.plugin.version}</version>
|
||||
<configuration>
|
||||
<excludeRoots>
|
||||
<excludeRoot>src/main/generated</excludeRoot>
|
||||
<excludeRoot>src/test</excludeRoot>
|
||||
</excludeRoots>
|
||||
<rulesets>
|
||||
<ruleset>/codestyle/pmd-rules.xml</ruleset>
|
||||
</rulesets>
|
||||
<targetJdk>${jdk.version}</targetJdk>
|
||||
<sourceEncoding>${project.build.sourceEncoding}</sourceEncoding>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
<goal>cpd-check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>se.jguru.nazgul.tools.codestyle</groupId>
|
||||
<artifactId>nazgul-codestyle</artifactId>
|
||||
<version>${nazgul-codestyle.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin -->
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<version>2.3.7</version>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<instructions>
|
||||
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
|
||||
<Export-Package>{local-packages}</Export-Package>
|
||||
</instructions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>${project.build.java.version}</source>
|
||||
<target>${project.build.java.version}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-release-plugin</artifactId>
|
||||
<version>2.4</version>
|
||||
<configuration>
|
||||
<mavenExecutorId>forked-path</mavenExecutorId>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<skipTests>false</skipTests>
|
||||
<excludes>
|
||||
<!-- exclude the suite which is a convenience class for running all tests from IDE or using scripts -->
|
||||
<exclude>AllTests.java</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- bind the source attaching to package phase -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<!--
|
||||
These two plugins take care of building and publishing the javadoc, using
|
||||
mvn clean javadoc:javadoc scm-publish:publish-scm
|
||||
-->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.9.1</version>
|
||||
<configuration>
|
||||
<aggregate>true</aggregate>
|
||||
<show>public</show>
|
||||
<nohelp>true</nohelp>
|
||||
<header>mbassador, ${project.version}</header>
|
||||
<footer>mbassador, ${project.version}</footer>
|
||||
<doctitle>mbassador, ${project.version}</doctitle>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-scm-publish-plugin</artifactId>
|
||||
<version>1.0-beta-2</version>
|
||||
<configuration>
|
||||
<checkoutDirectory>${project.build.directory}/scmpublish</checkoutDirectory>
|
||||
<checkinComment>Publishing javadoc for ${project.artifactId}:${project.version}</checkinComment>
|
||||
<content>${project.reporting.outputDirectory}/apidocs</content>
|
||||
<skipDeletedFiles>true</skipDeletedFiles>
|
||||
<pubScmUrl>scm:git:git@github.com:bennidi/mbassador.git</pubScmUrl>
|
||||
<scmBranch>gh-pages</scmBranch> <!-- branch with static site on github-->
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>release-sign-artifacts</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>performRelease</name>
|
||||
<value>true</value>
|
||||
</property>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<version>1.4</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>sign-artifacts</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>sign</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
|
||||
<parent>
|
||||
<groupId>org.sonatype.oss</groupId>
|
||||
<artifactId>oss-parent</artifactId>
|
||||
<version>7</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>net.engio</groupId>
|
||||
<artifactId>mbassador</artifactId>
|
||||
<version>1.1.11-SNAPSHOT</version>
|
||||
<packaging>bundle</packaging>
|
||||
<name>mbassador</name>
|
||||
<description>
|
||||
Mbassador is a fast and flexible message bus system following the publish subscribe pattern.
|
||||
It is designed for ease of use and aims to be feature rich and extensible
|
||||
while preserving resource efficiency and performance.
|
||||
|
||||
It features:
|
||||
declarative handler definition via annotations,
|
||||
sync and/or async message delivery,
|
||||
weak-references,
|
||||
message filtering,
|
||||
ordering of message handlers etc.
|
||||
</description>
|
||||
|
||||
<url>https://github.com/bennidi/mbassador</url>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>MIT license</name>
|
||||
<url>http://www.opensource.org/licenses/mit-license.php</url>
|
||||
</license>
|
||||
</licenses>
|
||||
<scm>
|
||||
<url>git@github.com:bennidi/mbassador.git</url>
|
||||
<connection>scm:git:git@github.com:bennidi/mbassador.git</connection>
|
||||
<tag>mbassador-1.1.4</tag>
|
||||
<developerConnection>scm:git:git@github.com:bennidi/mbassador.git</developerConnection>
|
||||
</scm>
|
||||
<developers>
|
||||
|
||||
<developer>
|
||||
<id>bennidi</id>
|
||||
<name>Benjamin Diedrichsen</name>
|
||||
<timezone>+1</timezone>
|
||||
<email>b.diedrichsen@googlemail.com</email>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<properties>
|
||||
<nazgul-codestyle.version>2.0.1</nazgul-codestyle.version>
|
||||
<jdk.version>1.6</jdk.version>
|
||||
<pmd.plugin.version>3.0.1</pmd.plugin.version>
|
||||
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.build.java.version>1.6</project.build.java.version>
|
||||
<github.url>file://${project.basedir}/mvn-local-repo</github.url>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.10</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>1.7.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
<version>1.7.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.el</groupId>
|
||||
<artifactId>el-api</artifactId>
|
||||
<version>2.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.odysseus.juel</groupId>
|
||||
<artifactId>juel-impl</artifactId>
|
||||
<version>2.2.7</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.odysseus.juel</groupId>
|
||||
<artifactId>juel-spi</artifactId>
|
||||
<version>2.2.7</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<!-- Local repository (for testing)
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>mbassador-github-repo</id>
|
||||
<url>${github.url}</url>
|
||||
</repository>
|
||||
</distributionManagement>
|
||||
-->
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-pmd-plugin</artifactId>
|
||||
<version>${pmd.plugin.version}</version>
|
||||
<configuration>
|
||||
<excludeRoots>
|
||||
<excludeRoot>src/main/generated</excludeRoot>
|
||||
<excludeRoot>src/test</excludeRoot>
|
||||
</excludeRoots>
|
||||
<rulesets>
|
||||
<ruleset>/codestyle/pmd-rules.xml</ruleset>
|
||||
</rulesets>
|
||||
<targetJdk>${jdk.version}</targetJdk>
|
||||
<sourceEncoding>${project.build.sourceEncoding}</sourceEncoding>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
<goal>cpd-check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>se.jguru.nazgul.tools.codestyle</groupId>
|
||||
<artifactId>nazgul-codestyle</artifactId>
|
||||
<version>${nazgul-codestyle.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin -->
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<version>2.3.7</version>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<instructions>
|
||||
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
|
||||
<Export-Package>{local-packages}</Export-Package>
|
||||
</instructions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>${project.build.java.version}</source>
|
||||
<target>${project.build.java.version}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-release-plugin</artifactId>
|
||||
<version>2.4</version>
|
||||
<configuration>
|
||||
<mavenExecutorId>forked-path</mavenExecutorId>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<skipTests>false</skipTests>
|
||||
<excludes>
|
||||
<!-- exclude the suite which is a convenience class for running all tests from IDE or using scripts -->
|
||||
<exclude>AllTests.java</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- bind the source attaching to package phase -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<!--
|
||||
These two plugins take care of building and publishing the javadoc, using
|
||||
mvn clean javadoc:javadoc scm-publish:publish-scm
|
||||
-->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.9.1</version>
|
||||
<configuration>
|
||||
<aggregate>true</aggregate>
|
||||
<show>public</show>
|
||||
<nohelp>true</nohelp>
|
||||
<header>mbassador, ${project.version}</header>
|
||||
<footer>mbassador, ${project.version}</footer>
|
||||
<doctitle>mbassador, ${project.version}</doctitle>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-scm-publish-plugin</artifactId>
|
||||
<version>1.0-beta-2</version>
|
||||
<configuration>
|
||||
<checkoutDirectory>${project.build.directory}/scmpublish</checkoutDirectory>
|
||||
<checkinComment>Publishing javadoc for ${project.artifactId}:${project.version}</checkinComment>
|
||||
<content>${project.reporting.outputDirectory}/apidocs</content>
|
||||
<skipDeletedFiles>true</skipDeletedFiles>
|
||||
<pubScmUrl>scm:git:git@github.com:bennidi/mbassador.git</pubScmUrl>
|
||||
<scmBranch>gh-pages</scmBranch> <!-- branch with static site on github-->
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>release-sign-artifacts</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>performRelease</name>
|
||||
<value>true</value>
|
||||
</property>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<version>1.4</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>sign-artifacts</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>sign</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
|
|
|
@ -1,45 +1,57 @@
|
|||
package net.engio.mbassy.dispatch;
|
||||
|
||||
import net.engio.mbassy.bus.MessagePublication;
|
||||
import net.engio.mbassy.listener.IMessageFilter;
|
||||
|
||||
/**
|
||||
* A dispatcher that implements message filtering based on the filter configuration
|
||||
* of the associated message handler. It will delegate message delivery to another
|
||||
* message dispatcher after having performed the filtering logic.
|
||||
*
|
||||
* @author bennidi
|
||||
* Date: 11/23/12
|
||||
*/
|
||||
public class FilteredMessageDispatcher extends DelegatingMessageDispatcher {
|
||||
|
||||
private final IMessageFilter[] filter;
|
||||
|
||||
public FilteredMessageDispatcher(IMessageDispatcher dispatcher) {
|
||||
super(dispatcher);
|
||||
this.filter = dispatcher.getContext().getHandlerMetadata().getFilter();
|
||||
}
|
||||
|
||||
private boolean passesFilter(Object message) {
|
||||
|
||||
if (filter == null) {
|
||||
return true;
|
||||
} else {
|
||||
for (IMessageFilter aFilter : filter) {
|
||||
if (!aFilter.accepts(message, getContext().getHandlerMetadata())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void dispatch(MessagePublication publication, Object message, Iterable listeners){
|
||||
if (passesFilter(message)) {
|
||||
getDelegate().dispatch(publication, message, listeners);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
package net.engio.mbassy.dispatch;
|
||||
|
||||
import net.engio.mbassy.bus.MessagePublication;
|
||||
import net.engio.mbassy.dispatch.el.ElFilter;
|
||||
import net.engio.mbassy.listener.IMessageFilter;
|
||||
|
||||
/**
|
||||
* A dispatcher that implements message filtering based on the filter configuration
|
||||
* of the associated message handler. It will delegate message delivery to another
|
||||
* message dispatcher after having performed the filtering logic.
|
||||
*
|
||||
* @author bennidi
|
||||
* Date: 11/23/12
|
||||
*/
|
||||
public class FilteredMessageDispatcher extends DelegatingMessageDispatcher {
|
||||
|
||||
private final IMessageFilter[] filter;
|
||||
|
||||
public FilteredMessageDispatcher(IMessageDispatcher dispatcher) {
|
||||
super(dispatcher);
|
||||
this.filter = dispatcher.getContext().getHandlerMetadata().getFilter();
|
||||
}
|
||||
|
||||
private boolean passesFilter(Object message) {
|
||||
|
||||
if (filter == null) {
|
||||
return true;
|
||||
} else {
|
||||
for (IMessageFilter aFilter : filter) {
|
||||
if (!aFilter.accepts(message, getContext().getHandlerMetadata())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void dispatch(MessagePublication publication, Object message, Iterable listeners){
|
||||
if (passesFilter(message) && passesELFilter(message)) {
|
||||
getDelegate().dispatch(publication, message, listeners);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* This will test the EL expression defined on the Handler annotation.
|
||||
* This is like a "parameterizable" filter.
|
||||
* @param me the message object to filter with the EL expression if there is one.
|
||||
* @return true if the event is allowed, false if it is rejected.
|
||||
************************************************************************/
|
||||
|
||||
private boolean passesELFilter(Object message) {
|
||||
ElFilter filter = ElFilter.getInstance();
|
||||
return filter != null && filter.accepts(message, getContext().getHandlerMetadata());
|
||||
}
|
||||
}
|
||||
|
|
113
src/main/java/net/engio/mbassy/dispatch/el/ElFilter.java
Normal file
113
src/main/java/net/engio/mbassy/dispatch/el/ElFilter.java
Normal file
|
@ -0,0 +1,113 @@
|
|||
package net.engio.mbassy.dispatch.el;
|
||||
|
||||
import javax.el.ExpressionFactory;
|
||||
import javax.el.ValueExpression;
|
||||
|
||||
import net.engio.mbassy.listener.IMessageFilter;
|
||||
import net.engio.mbassy.listener.MessageHandler;
|
||||
|
||||
/*****************************************************************************
|
||||
* A filter that will use a expression from the handler annotation and
|
||||
* parse it as EL.
|
||||
****************************************************************************/
|
||||
|
||||
public class ElFilter implements IMessageFilter {
|
||||
|
||||
private static ElFilter instance;
|
||||
|
||||
static {
|
||||
try {
|
||||
instance = new ElFilter();
|
||||
} catch (Exception e) {
|
||||
// Most likely the javax.el package is not available.
|
||||
instance = null;
|
||||
}
|
||||
}
|
||||
|
||||
private ExpressionFactory elFactory;
|
||||
|
||||
/*************************************************************************
|
||||
* Constructor
|
||||
************************************************************************/
|
||||
|
||||
private ElFilter() {
|
||||
super();
|
||||
initELFactory();
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* Get an implementation of the ExpressionFactory. This uses the
|
||||
* Java service lookup mechanism to find a proper implementation.
|
||||
* If none if available we do not support EL filters.
|
||||
************************************************************************/
|
||||
|
||||
private void initELFactory() {
|
||||
try {
|
||||
this.elFactory = ExpressionFactory.newInstance();
|
||||
} catch (RuntimeException e) {
|
||||
// No EL implementation on the class path.
|
||||
elFactory = null;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* accepts
|
||||
* @see net.engio.mbassy.listener.IMessageFilter#accepts(java.lang.Object, net.engio.mbassy.listener.MessageHandler)
|
||||
************************************************************************/
|
||||
@Override
|
||||
public boolean accepts(Object message, MessageHandler metadata) {
|
||||
String expression = metadata.getCondition();
|
||||
if (expression == null || expression.trim().length() == 0) {
|
||||
return true;
|
||||
}
|
||||
if (elFactory == null) {
|
||||
// TODO should we test this some where earlier? Perhaps in MessageHandler.validate() ?
|
||||
throw new IllegalStateException("A handler uses an EL filter but no EL implementation is available.");
|
||||
}
|
||||
|
||||
expression = cleanupExpression(expression);
|
||||
|
||||
EventContext context = new EventContext();
|
||||
context.bindToEvent(message);
|
||||
|
||||
return evalExpression(expression, context);
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* @param expression
|
||||
* @param context
|
||||
* @return
|
||||
************************************************************************/
|
||||
|
||||
private boolean evalExpression(String expression, EventContext context) {
|
||||
ValueExpression ve = elFactory.createValueExpression(context, expression, Boolean.class);
|
||||
Object result = ve.getValue(context);
|
||||
if (!(result instanceof Boolean)) {
|
||||
throw new IllegalStateException("A handler uses an EL filter but the output is not \"true\" or \"false\".");
|
||||
}
|
||||
return (Boolean)result;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* Make it a valid expression because the parser expects it like this.
|
||||
* @param expression
|
||||
* @return
|
||||
************************************************************************/
|
||||
|
||||
private String cleanupExpression(String expression) {
|
||||
|
||||
if (!expression.trim().startsWith("${") && !expression.trim().startsWith("#{")) {
|
||||
expression = "${"+expression+"}";
|
||||
}
|
||||
return expression;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* @return the one and only
|
||||
************************************************************************/
|
||||
|
||||
public static synchronized ElFilter getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
}
|
102
src/main/java/net/engio/mbassy/dispatch/el/EventContext.java
Normal file
102
src/main/java/net/engio/mbassy/dispatch/el/EventContext.java
Normal file
|
@ -0,0 +1,102 @@
|
|||
package net.engio.mbassy.dispatch.el;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import javax.el.BeanELResolver;
|
||||
import javax.el.CompositeELResolver;
|
||||
import javax.el.ELContext;
|
||||
import javax.el.ELResolver;
|
||||
import javax.el.FunctionMapper;
|
||||
import javax.el.ValueExpression;
|
||||
import javax.el.VariableMapper;
|
||||
|
||||
/*****************************************************************************
|
||||
* An EL context that knows how to resolve everything from a
|
||||
* given message but event.
|
||||
****************************************************************************/
|
||||
|
||||
public class EventContext extends ELContext {
|
||||
|
||||
private final CompositeELResolver resolver;
|
||||
private final FunctionMapper functionMapper;
|
||||
private final VariableMapper variableMapper;
|
||||
private RootResolver rootResolver;
|
||||
|
||||
/*************************************************************************
|
||||
* Constructor
|
||||
*
|
||||
* @param me
|
||||
************************************************************************/
|
||||
|
||||
public EventContext() {
|
||||
super();
|
||||
this.functionMapper = new NoopFunctionMapper();
|
||||
this.variableMapper = new NoopMapperImpl();
|
||||
|
||||
this.resolver = new CompositeELResolver();
|
||||
this.rootResolver = new RootResolver();
|
||||
this.resolver.add(rootResolver);
|
||||
this.resolver.add(new BeanELResolver(true));
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* Binds an event object with the EL expression. This will allow access
|
||||
* to all properties of a given event.
|
||||
* @param event to bind.
|
||||
************************************************************************/
|
||||
|
||||
public void bindToEvent(Object event) {
|
||||
this.rootResolver.setRoot(event);
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* The resolver for the event object.
|
||||
* @see javax.el.ELContext#getELResolver()
|
||||
************************************************************************/
|
||||
@Override
|
||||
public ELResolver getELResolver() {
|
||||
return this.resolver;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* @see javax.el.ELContext#getFunctionMapper()
|
||||
************************************************************************/
|
||||
@Override
|
||||
public FunctionMapper getFunctionMapper() {
|
||||
return this.functionMapper;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* @see javax.el.ELContext#getVariableMapper()
|
||||
************************************************************************/
|
||||
@Override
|
||||
public VariableMapper getVariableMapper() {
|
||||
return this.variableMapper;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* Dummy implementation.
|
||||
****************************************************************************/
|
||||
|
||||
private class NoopMapperImpl extends VariableMapper {
|
||||
public ValueExpression resolveVariable(String s) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public ValueExpression setVariable(String s,
|
||||
ValueExpression valueExpression) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* Dummy implementation.
|
||||
****************************************************************************/
|
||||
|
||||
private class NoopFunctionMapper extends FunctionMapper {
|
||||
public Method resolveFunction(String s, String s1) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
89
src/main/java/net/engio/mbassy/dispatch/el/RootResolver.java
Normal file
89
src/main/java/net/engio/mbassy/dispatch/el/RootResolver.java
Normal file
|
@ -0,0 +1,89 @@
|
|||
package net.engio.mbassy.dispatch.el;
|
||||
|
||||
import java.beans.FeatureDescriptor;
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.el.ELContext;
|
||||
import javax.el.ELResolver;
|
||||
|
||||
/*****************************************************************************
|
||||
* A resolver that will resolve the "msg" variable to the event object that
|
||||
* is posted.
|
||||
****************************************************************************/
|
||||
|
||||
public class RootResolver extends ELResolver {
|
||||
|
||||
private static final String ROOT_VAR_NAME = "msg";
|
||||
public Object rootObject;
|
||||
|
||||
/*************************************************************************
|
||||
* @param rootObject
|
||||
************************************************************************/
|
||||
|
||||
public void setRoot(Object rootObject) {
|
||||
this.rootObject = rootObject;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* getValue
|
||||
* @see javax.el.ELResolver#getValue(javax.el.ELContext, java.lang.Object, java.lang.Object)
|
||||
************************************************************************/
|
||||
@Override
|
||||
public Object getValue(ELContext context, Object base, Object property) {
|
||||
if (context == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
if (base == null && ROOT_VAR_NAME.equals(property)) {
|
||||
context.setPropertyResolved(true);
|
||||
return this.rootObject;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
* getCommonPropertyType
|
||||
* @see javax.el.ELResolver#getCommonPropertyType(javax.el.ELContext, java.lang.Object)
|
||||
************************************************************************/
|
||||
@Override
|
||||
public Class<?> getCommonPropertyType(ELContext context, Object base) {
|
||||
return String.class;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* getFeatureDescriptors
|
||||
* @see javax.el.ELResolver#getFeatureDescriptors(javax.el.ELContext, java.lang.Object)
|
||||
************************************************************************/
|
||||
@Override
|
||||
public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* getType
|
||||
* @see javax.el.ELResolver#getType(javax.el.ELContext, java.lang.Object, java.lang.Object)
|
||||
************************************************************************/
|
||||
@Override
|
||||
public Class<?> getType(ELContext context, Object base, Object property) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* isReadOnly
|
||||
* @see javax.el.ELResolver#isReadOnly(javax.el.ELContext, java.lang.Object, java.lang.Object)
|
||||
************************************************************************/
|
||||
@Override
|
||||
public boolean isReadOnly(ELContext context, Object base, Object property) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* setValue
|
||||
* @see javax.el.ELResolver#setValue(javax.el.ELContext, java.lang.Object, java.lang.Object, java.lang.Object)
|
||||
************************************************************************/
|
||||
@Override
|
||||
public void setValue(ELContext context, Object base, Object property, Object value) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
}
|
|
@ -1,66 +1,76 @@
|
|||
package net.engio.mbassy.listener;
|
||||
|
||||
import net.engio.mbassy.dispatch.HandlerInvocation;
|
||||
import net.engio.mbassy.dispatch.ReflectiveHandlerInvocation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Mark any method of any class(=listener) as a message handler and configure the handler
|
||||
* using different properties.
|
||||
*
|
||||
* @author bennidi
|
||||
* Date: 2/8/12
|
||||
*/
|
||||
@Retention(value = RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Target(value = {ElementType.METHOD})
|
||||
public @interface Handler {
|
||||
|
||||
/**
|
||||
* Add any numbers of filters to the handler. All filters are evaluated before the handler
|
||||
* is actually invoked, which is only if all the filters accept the message.
|
||||
*/
|
||||
Filter[] filters() default {};
|
||||
|
||||
/**
|
||||
* Define the mode in which a message is delivered to each listener. Listeners can be notified
|
||||
* sequentially or concurrently.
|
||||
*/
|
||||
Invoke delivery() default Invoke.Synchronously;
|
||||
|
||||
/**
|
||||
* Handlers are ordered by priority and handlers with higher priority are processed before
|
||||
* those with lower priority, i.e. Influence the order in which different handlers that consume
|
||||
* the same message type are invoked.
|
||||
*/
|
||||
int priority() default 0;
|
||||
|
||||
/**
|
||||
* Define whether or not the handler accepts sub types of the message type it declares in its
|
||||
* signature.
|
||||
*/
|
||||
boolean rejectSubtypes() default false;
|
||||
|
||||
|
||||
/**
|
||||
* Enable or disable the handler. Disabled handlers do not receive any messages.
|
||||
* This property is useful for quick changes in configuration and necessary to disable
|
||||
* handlers that have been declared by a superclass but do not apply to the subclass
|
||||
*/
|
||||
boolean enabled() default true;
|
||||
|
||||
|
||||
/**
|
||||
* Each handler call is implemented as an invocation object that implements the invocation mechanism.
|
||||
* The basic implementation uses reflection and is the default. It is possible though to provide a custom
|
||||
* invocation to add additional logic.
|
||||
*
|
||||
* Note: Providing a custom invocation will most likely reduce performance, since the JIT-Compiler
|
||||
* can not do some of its sophisticated byte code optimizations.
|
||||
*
|
||||
*/
|
||||
Class<? extends HandlerInvocation> invocation() default ReflectiveHandlerInvocation.class;
|
||||
|
||||
|
||||
}
|
||||
package net.engio.mbassy.listener;
|
||||
|
||||
import net.engio.mbassy.dispatch.HandlerInvocation;
|
||||
import net.engio.mbassy.dispatch.ReflectiveHandlerInvocation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Mark any method of any class(=listener) as a message handler and configure the handler
|
||||
* using different properties.
|
||||
*
|
||||
* @author bennidi
|
||||
* Date: 2/8/12
|
||||
*/
|
||||
@Retention(value = RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Target(value = {ElementType.METHOD})
|
||||
public @interface Handler {
|
||||
|
||||
/**
|
||||
* Add any numbers of filters to the handler. All filters are evaluated before the handler
|
||||
* is actually invoked, which is only if all the filters accept the message.
|
||||
*/
|
||||
Filter[] filters() default {};
|
||||
|
||||
|
||||
/**
|
||||
* Defines a filter condition as Expression Language. This can be used to filter the events based on
|
||||
* attributes of the event object. Note that the expression must resolve to either
|
||||
* <code>true</code> to allow the event or <code>false</code> to block it from delivery to the handler.
|
||||
* The message itself is available as "msg" variable.
|
||||
* @return the condition in EL syntax.
|
||||
*/
|
||||
String condition() default "";
|
||||
|
||||
/**
|
||||
* Define the mode in which a message is delivered to each listener. Listeners can be notified
|
||||
* sequentially or concurrently.
|
||||
*/
|
||||
Invoke delivery() default Invoke.Synchronously;
|
||||
|
||||
/**
|
||||
* Handlers are ordered by priority and handlers with higher priority are processed before
|
||||
* those with lower priority, i.e. Influence the order in which different handlers that consume
|
||||
* the same message type are invoked.
|
||||
*/
|
||||
int priority() default 0;
|
||||
|
||||
/**
|
||||
* Define whether or not the handler accepts sub types of the message type it declares in its
|
||||
* signature.
|
||||
*/
|
||||
boolean rejectSubtypes() default false;
|
||||
|
||||
|
||||
/**
|
||||
* Enable or disable the handler. Disabled handlers do not receive any messages.
|
||||
* This property is useful for quick changes in configuration and necessary to disable
|
||||
* handlers that have been declared by a superclass but do not apply to the subclass
|
||||
*/
|
||||
boolean enabled() default true;
|
||||
|
||||
|
||||
/**
|
||||
* Each handler call is implemented as an invocation object that implements the invocation mechanism.
|
||||
* The basic implementation uses reflection and is the default. It is possible though to provide a custom
|
||||
* invocation to add additional logic.
|
||||
*
|
||||
* Note: Providing a custom invocation will most likely reduce performance, since the JIT-Compiler
|
||||
* can not do some of its sophisticated byte code optimizations.
|
||||
*
|
||||
*/
|
||||
Class<? extends HandlerInvocation> invocation() default ReflectiveHandlerInvocation.class;
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,183 +1,194 @@
|
|||
package net.engio.mbassy.listener;
|
||||
|
||||
import net.engio.mbassy.dispatch.HandlerInvocation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Any method in any class annotated with the @Handler annotation represents a message handler. The class that contains
|
||||
* the handler is called a message listener and more generally, any class containing a message handler in its class hierarchy
|
||||
* defines such a message listener.
|
||||
*
|
||||
* @author bennidi
|
||||
* Date: 11/14/12
|
||||
*/
|
||||
public class MessageHandler {
|
||||
|
||||
public static final class Properties{
|
||||
|
||||
public static final String HandlerMethod = "handler";
|
||||
public static final String InvocationMode = "invocationMode";
|
||||
public static final String Filter = "filter";
|
||||
public static final String Enveloped = "envelope";
|
||||
public static final String HandledMessages = "messages";
|
||||
public static final String IsSynchronized = "synchronized";
|
||||
public static final String Listener = "listener";
|
||||
public static final String AcceptSubtypes = "subtypes";
|
||||
public static final String Priority = "priority";
|
||||
public static final String Invocation = "invocation";
|
||||
|
||||
/**
|
||||
* Create the property map for the {@link MessageHandler} constructor using the default objects.
|
||||
*
|
||||
* @param handler The handler annotated method of the listener
|
||||
* @param handlerConfig The annotation that configures the handler
|
||||
* @param filter The set of preconfigured filters if any
|
||||
* @param listenerConfig The listener metadata
|
||||
* @return A map of properties initialized from the given parameters that will conform to the requirements of the
|
||||
* {@link MessageHandler} constructor. See {@see MessageHandler.validate()} for more details.
|
||||
*/
|
||||
public static final Map<String, Object> Create(Method handler, Handler handlerConfig, IMessageFilter[] filter, MessageListener listenerConfig){
|
||||
if(handler == null){
|
||||
throw new IllegalArgumentException("The message handler configuration may not be null");
|
||||
}
|
||||
net.engio.mbassy.listener.Enveloped enveloped = handler.getAnnotation(Enveloped.class);
|
||||
Class[] handledMessages = enveloped != null
|
||||
? enveloped.messages()
|
||||
: handler.getParameterTypes();
|
||||
handler.setAccessible(true);
|
||||
Map<String, Object> properties = new HashMap<String, Object>();
|
||||
properties.put(HandlerMethod, handler);
|
||||
properties.put(Filter, filter != null ? filter : new IMessageFilter[]{});
|
||||
properties.put(Priority, handlerConfig.priority());
|
||||
properties.put(Invocation, handlerConfig.invocation());
|
||||
properties.put(InvocationMode, handlerConfig.delivery());
|
||||
properties.put(Enveloped, enveloped != null);
|
||||
properties.put(AcceptSubtypes, !handlerConfig.rejectSubtypes());
|
||||
properties.put(Listener, listenerConfig);
|
||||
properties.put(IsSynchronized, handler.getAnnotation(Synchronized.class) != null);
|
||||
properties.put(HandledMessages, handledMessages);
|
||||
return properties;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private final Method handler;
|
||||
|
||||
private final IMessageFilter[] filter;
|
||||
|
||||
private final int priority;
|
||||
|
||||
private final Class<? extends HandlerInvocation> invocation;
|
||||
|
||||
private final Invoke invocationMode;
|
||||
|
||||
private final boolean isEnvelope;
|
||||
|
||||
private final Class[] handledMessages;
|
||||
|
||||
private final boolean acceptsSubtypes;
|
||||
|
||||
private final MessageListener listenerConfig;
|
||||
|
||||
private final boolean isSynchronized;
|
||||
|
||||
public MessageHandler(Map<String, Object> properties){
|
||||
super();
|
||||
validate(properties);
|
||||
this.handler = (Method)properties.get(Properties.HandlerMethod);
|
||||
this.filter = (IMessageFilter[])properties.get(Properties.Filter);
|
||||
this.priority = (Integer)properties.get(Properties.Priority);
|
||||
this.invocation = (Class<? extends HandlerInvocation>)properties.get(Properties.Invocation);
|
||||
this.invocationMode = (Invoke)properties.get(Properties.InvocationMode);
|
||||
this.isEnvelope = (Boolean)properties.get(Properties.Enveloped);
|
||||
this.acceptsSubtypes = (Boolean)properties.get(Properties.AcceptSubtypes);
|
||||
this.listenerConfig = (MessageListener)properties.get(Properties.Listener);
|
||||
this.isSynchronized = (Boolean)properties.get(Properties.IsSynchronized);
|
||||
this.handledMessages = (Class[])properties.get(Properties.HandledMessages);
|
||||
}
|
||||
|
||||
private void validate(Map<String, Object> properties){
|
||||
Object[][] expectedProperties = new Object[][]{
|
||||
new Object[]{Properties.HandlerMethod, Method.class },
|
||||
new Object[]{Properties.Priority, Integer.class },
|
||||
new Object[]{Properties.Invocation, Class.class },
|
||||
new Object[]{Properties.Filter, IMessageFilter[].class },
|
||||
new Object[]{Properties.Enveloped, Boolean.class },
|
||||
new Object[]{Properties.HandledMessages, Class[].class },
|
||||
new Object[]{Properties.IsSynchronized, Boolean.class },
|
||||
new Object[]{Properties.Listener, MessageListener.class },
|
||||
new Object[]{Properties.AcceptSubtypes, Boolean.class }
|
||||
};
|
||||
for(Object[] property : expectedProperties){
|
||||
if (properties.get(property[0]) == null || !((Class)property[1]).isAssignableFrom(properties.get(property[0]).getClass()))
|
||||
throw new IllegalArgumentException("Property " + property[0] + " was expected to be not null and of type " + property[1]
|
||||
+ " but was: " + properties.get(property[0]));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public boolean isSynchronized(){
|
||||
return isSynchronized;
|
||||
}
|
||||
|
||||
public boolean useStrongReferences(){
|
||||
return listenerConfig.useStrongReferences();
|
||||
}
|
||||
|
||||
public boolean isFromListener(Class listener){
|
||||
return listenerConfig.isFromListener(listener);
|
||||
}
|
||||
|
||||
public boolean isAsynchronous() {
|
||||
return invocationMode.equals(Invoke.Asynchronously);
|
||||
}
|
||||
|
||||
public boolean isFiltered() {
|
||||
return filter.length > 0;
|
||||
}
|
||||
|
||||
public int getPriority() {
|
||||
return priority;
|
||||
}
|
||||
|
||||
public Method getHandler() {
|
||||
return handler;
|
||||
}
|
||||
|
||||
public IMessageFilter[] getFilter() {
|
||||
return filter;
|
||||
}
|
||||
|
||||
public Class[] getHandledMessages() {
|
||||
return handledMessages;
|
||||
}
|
||||
|
||||
public boolean isEnveloped() {
|
||||
return isEnvelope;
|
||||
}
|
||||
|
||||
public Class<? extends HandlerInvocation> getHandlerInvocation(){
|
||||
return invocation;
|
||||
}
|
||||
|
||||
public boolean handlesMessage(Class<?> messageType) {
|
||||
for (Class<?> handledMessage : handledMessages) {
|
||||
if (handledMessage.equals(messageType)) {
|
||||
return true;
|
||||
}
|
||||
if (handledMessage.isAssignableFrom(messageType) && acceptsSubtypes()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean acceptsSubtypes() {
|
||||
return acceptsSubtypes;
|
||||
}
|
||||
|
||||
}
|
||||
package net.engio.mbassy.listener;
|
||||
|
||||
import net.engio.mbassy.dispatch.HandlerInvocation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Any method in any class annotated with the @Handler annotation represents a message handler. The class that contains
|
||||
* the handler is called a message listener and more generally, any class containing a message handler in its class hierarchy
|
||||
* defines such a message listener.
|
||||
*
|
||||
* @author bennidi
|
||||
* Date: 11/14/12
|
||||
*/
|
||||
public class MessageHandler {
|
||||
|
||||
public static final class Properties{
|
||||
|
||||
public static final String HandlerMethod = "handler";
|
||||
public static final String InvocationMode = "invocationMode";
|
||||
public static final String Filter = "filter";
|
||||
public static final String Condition = "condition";
|
||||
public static final String Enveloped = "envelope";
|
||||
public static final String HandledMessages = "messages";
|
||||
public static final String IsSynchronized = "synchronized";
|
||||
public static final String Listener = "listener";
|
||||
public static final String AcceptSubtypes = "subtypes";
|
||||
public static final String Priority = "priority";
|
||||
public static final String Invocation = "invocation";
|
||||
|
||||
/**
|
||||
* Create the property map for the {@link MessageHandler} constructor using the default objects.
|
||||
*
|
||||
* @param handler The handler annotated method of the listener
|
||||
* @param handlerConfig The annotation that configures the handler
|
||||
* @param filter The set of preconfigured filters if any
|
||||
* @param listenerConfig The listener metadata
|
||||
* @return A map of properties initialized from the given parameters that will conform to the requirements of the
|
||||
* {@link MessageHandler} constructor. See {@see MessageHandler.validate()} for more details.
|
||||
*/
|
||||
public static final Map<String, Object> Create(Method handler, Handler handlerConfig, IMessageFilter[] filter, MessageListener listenerConfig){
|
||||
if(handler == null){
|
||||
throw new IllegalArgumentException("The message handler configuration may not be null");
|
||||
}
|
||||
net.engio.mbassy.listener.Enveloped enveloped = handler.getAnnotation(Enveloped.class);
|
||||
Class[] handledMessages = enveloped != null
|
||||
? enveloped.messages()
|
||||
: handler.getParameterTypes();
|
||||
handler.setAccessible(true);
|
||||
Map<String, Object> properties = new HashMap<String, Object>();
|
||||
properties.put(HandlerMethod, handler);
|
||||
properties.put(Filter, filter != null ? filter : new IMessageFilter[]{});
|
||||
properties.put(Condition, handlerConfig.condition());
|
||||
properties.put(Priority, handlerConfig.priority());
|
||||
properties.put(Invocation, handlerConfig.invocation());
|
||||
properties.put(InvocationMode, handlerConfig.delivery());
|
||||
properties.put(Enveloped, enveloped != null);
|
||||
properties.put(AcceptSubtypes, !handlerConfig.rejectSubtypes());
|
||||
properties.put(Listener, listenerConfig);
|
||||
properties.put(IsSynchronized, handler.getAnnotation(Synchronized.class) != null);
|
||||
properties.put(HandledMessages, handledMessages);
|
||||
return properties;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private final Method handler;
|
||||
|
||||
private final IMessageFilter[] filter;
|
||||
|
||||
private String condition;
|
||||
|
||||
private final int priority;
|
||||
|
||||
private final Class<? extends HandlerInvocation> invocation;
|
||||
|
||||
private final Invoke invocationMode;
|
||||
|
||||
private final boolean isEnvelope;
|
||||
|
||||
private final Class[] handledMessages;
|
||||
|
||||
private final boolean acceptsSubtypes;
|
||||
|
||||
private final MessageListener listenerConfig;
|
||||
|
||||
private final boolean isSynchronized;
|
||||
|
||||
|
||||
public MessageHandler(Map<String, Object> properties){
|
||||
super();
|
||||
validate(properties);
|
||||
this.handler = (Method)properties.get(Properties.HandlerMethod);
|
||||
this.filter = (IMessageFilter[])properties.get(Properties.Filter);
|
||||
this.condition = (String)properties.get(Properties.Condition);
|
||||
this.priority = (Integer)properties.get(Properties.Priority);
|
||||
this.invocation = (Class<? extends HandlerInvocation>)properties.get(Properties.Invocation);
|
||||
this.invocationMode = (Invoke)properties.get(Properties.InvocationMode);
|
||||
this.isEnvelope = (Boolean)properties.get(Properties.Enveloped);
|
||||
this.acceptsSubtypes = (Boolean)properties.get(Properties.AcceptSubtypes);
|
||||
this.listenerConfig = (MessageListener)properties.get(Properties.Listener);
|
||||
this.isSynchronized = (Boolean)properties.get(Properties.IsSynchronized);
|
||||
this.handledMessages = (Class[])properties.get(Properties.HandledMessages);
|
||||
}
|
||||
|
||||
private void validate(Map<String, Object> properties){
|
||||
Object[][] expectedProperties = new Object[][]{
|
||||
new Object[]{Properties.HandlerMethod, Method.class },
|
||||
new Object[]{Properties.Priority, Integer.class },
|
||||
new Object[]{Properties.Invocation, Class.class },
|
||||
new Object[]{Properties.Filter, IMessageFilter[].class },
|
||||
new Object[]{Properties.Condition, String.class },
|
||||
new Object[]{Properties.Enveloped, Boolean.class },
|
||||
new Object[]{Properties.HandledMessages, Class[].class },
|
||||
new Object[]{Properties.IsSynchronized, Boolean.class },
|
||||
new Object[]{Properties.Listener, MessageListener.class },
|
||||
new Object[]{Properties.AcceptSubtypes, Boolean.class }
|
||||
};
|
||||
for(Object[] property : expectedProperties){
|
||||
if (properties.get(property[0]) == null || !((Class)property[1]).isAssignableFrom(properties.get(property[0]).getClass()))
|
||||
throw new IllegalArgumentException("Property " + property[0] + " was expected to be not null and of type " + property[1]
|
||||
+ " but was: " + properties.get(property[0]));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public boolean isSynchronized(){
|
||||
return isSynchronized;
|
||||
}
|
||||
|
||||
public boolean useStrongReferences(){
|
||||
return listenerConfig.useStrongReferences();
|
||||
}
|
||||
|
||||
public boolean isFromListener(Class listener){
|
||||
return listenerConfig.isFromListener(listener);
|
||||
}
|
||||
|
||||
public boolean isAsynchronous() {
|
||||
return invocationMode.equals(Invoke.Asynchronously);
|
||||
}
|
||||
|
||||
public boolean isFiltered() {
|
||||
return filter.length > 0 || (condition != null && condition.trim().length() > 0);
|
||||
}
|
||||
|
||||
public int getPriority() {
|
||||
return priority;
|
||||
}
|
||||
|
||||
public Method getHandler() {
|
||||
return handler;
|
||||
}
|
||||
|
||||
public IMessageFilter[] getFilter() {
|
||||
return filter;
|
||||
}
|
||||
|
||||
public String getCondition() {
|
||||
return this.condition;
|
||||
}
|
||||
|
||||
public Class[] getHandledMessages() {
|
||||
return handledMessages;
|
||||
}
|
||||
|
||||
public boolean isEnveloped() {
|
||||
return isEnvelope;
|
||||
}
|
||||
|
||||
public Class<? extends HandlerInvocation> getHandlerInvocation(){
|
||||
return invocation;
|
||||
}
|
||||
|
||||
public boolean handlesMessage(Class<?> messageType) {
|
||||
for (Class<?> handledMessage : handledMessages) {
|
||||
if (handledMessage.equals(messageType)) {
|
||||
return true;
|
||||
}
|
||||
if (handledMessage.isAssignableFrom(messageType) && acceptsSubtypes()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean acceptsSubtypes() {
|
||||
return acceptsSubtypes;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
133
src/test/java/net/engio/mbassy/ConditionTest.java
Normal file
133
src/test/java/net/engio/mbassy/ConditionTest.java
Normal file
|
@ -0,0 +1,133 @@
|
|||
package net.engio.mbassy;
|
||||
|
||||
import net.engio.mbassy.bus.MBassador;
|
||||
import net.engio.mbassy.bus.config.BusConfiguration;
|
||||
import net.engio.mbassy.common.MessageBusTest;
|
||||
import net.engio.mbassy.listener.Handler;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/*****************************************************************************
|
||||
* Some unit tests for the "condition" filter.
|
||||
****************************************************************************/
|
||||
|
||||
public class ConditionTest extends MessageBusTest {
|
||||
|
||||
public static class TestEvent {
|
||||
|
||||
public Object result;
|
||||
private String type;
|
||||
private int size;
|
||||
|
||||
public TestEvent(String type, int size) {
|
||||
super();
|
||||
this.type = type;
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class ConditionalMessageListener {
|
||||
|
||||
@Handler(condition = "msg.type == \"TEST\"")
|
||||
public void handleTypeMessage(TestEvent message) {
|
||||
message.result = "handleTypeMessage";
|
||||
}
|
||||
|
||||
@Handler(condition = "msg.size > 4")
|
||||
public void handleSizeMessage(TestEvent message) {
|
||||
message.result = "handleSizeMessage";
|
||||
}
|
||||
|
||||
@Handler(condition = "msg.size > 2 && msg.size < 4")
|
||||
public void handleCombinedEL(TestEvent message) {
|
||||
message.result = "handleCombinedEL";
|
||||
}
|
||||
|
||||
@Handler(condition = "msg.getType().equals(\"XYZ\") && msg.getSize() == 1")
|
||||
public void handleMethodAccessEL(TestEvent message) {
|
||||
message.result = "handleMethodAccessEL";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* @throws Exception
|
||||
************************************************************************/
|
||||
@Test
|
||||
public void testSimpleStringCondition() throws Exception {
|
||||
MBassador bus = getBus(BusConfiguration.Default());
|
||||
bus.subscribe(new ConditionalMessageListener());
|
||||
|
||||
TestEvent message = new TestEvent("TEST", 0);
|
||||
bus.publish(message);
|
||||
|
||||
assertEquals("handleTypeMessage", message.result);
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* @throws Exception
|
||||
************************************************************************/
|
||||
@Test
|
||||
public void testSimpleNumberCondition() throws Exception {
|
||||
MBassador bus = getBus(BusConfiguration.Default());
|
||||
bus.subscribe(new ConditionalMessageListener());
|
||||
|
||||
TestEvent message = new TestEvent("", 5);
|
||||
bus.publish(message);
|
||||
|
||||
assertEquals("handleSizeMessage", message.result);
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* @throws Exception
|
||||
************************************************************************/
|
||||
@Test
|
||||
public void testHandleCombinedEL() throws Exception {
|
||||
MBassador bus = getBus(BusConfiguration.Default());
|
||||
bus.subscribe(new ConditionalMessageListener());
|
||||
|
||||
TestEvent message = new TestEvent("", 3);
|
||||
bus.publish(message);
|
||||
|
||||
assertEquals("handleCombinedEL", message.result);
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* @throws Exception
|
||||
************************************************************************/
|
||||
@Test
|
||||
public void testNotMatchingAnyCondition() throws Exception {
|
||||
MBassador bus = getBus(BusConfiguration.Default());
|
||||
bus.subscribe(new ConditionalMessageListener());
|
||||
|
||||
TestEvent message = new TestEvent("", 0);
|
||||
bus.publish(message);
|
||||
|
||||
assertTrue(message.result == null);
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* @throws Exception
|
||||
************************************************************************/
|
||||
@Test
|
||||
public void testHandleMethodAccessEL() throws Exception {
|
||||
MBassador bus = getBus(BusConfiguration.Default());
|
||||
bus.subscribe(new ConditionalMessageListener());
|
||||
|
||||
TestEvent message = new TestEvent("XYZ", 1);
|
||||
bus.publish(message);
|
||||
|
||||
assertEquals("handleMethodAccessEL", message.result);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user