Merge pull request #70 from Rossi1337/master
Support Message filtering per Expression Language
This commit is contained in:
commit
86bdcad336
541
pom.xml
541
pom.xml
|
@ -1,261 +1,280 @@
|
||||||
<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">
|
<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>
|
<parent>
|
||||||
<groupId>org.sonatype.oss</groupId>
|
<groupId>org.sonatype.oss</groupId>
|
||||||
<artifactId>oss-parent</artifactId>
|
<artifactId>oss-parent</artifactId>
|
||||||
<version>7</version>
|
<version>7</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>net.engio</groupId>
|
<groupId>net.engio</groupId>
|
||||||
<artifactId>mbassador</artifactId>
|
<artifactId>mbassador</artifactId>
|
||||||
<version>1.1.11-SNAPSHOT</version>
|
<version>1.1.11-SNAPSHOT</version>
|
||||||
<packaging>bundle</packaging>
|
<packaging>bundle</packaging>
|
||||||
<name>mbassador</name>
|
<name>mbassador</name>
|
||||||
<description>
|
<description>
|
||||||
Mbassador is a fast and flexible message bus system following the publish subscribe pattern.
|
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
|
It is designed for ease of use and aims to be feature rich and extensible
|
||||||
while preserving resource efficiency and performance.
|
while preserving resource efficiency and performance.
|
||||||
|
|
||||||
It features:
|
It features:
|
||||||
declarative handler definition via annotations,
|
declarative handler definition via annotations,
|
||||||
sync and/or async message delivery,
|
sync and/or async message delivery,
|
||||||
weak-references,
|
weak-references,
|
||||||
message filtering,
|
message filtering,
|
||||||
ordering of message handlers etc.
|
ordering of message handlers etc.
|
||||||
</description>
|
</description>
|
||||||
|
|
||||||
<url>https://github.com/bennidi/mbassador</url>
|
<url>https://github.com/bennidi/mbassador</url>
|
||||||
<licenses>
|
<licenses>
|
||||||
<license>
|
<license>
|
||||||
<name>MIT license</name>
|
<name>MIT license</name>
|
||||||
<url>http://www.opensource.org/licenses/mit-license.php</url>
|
<url>http://www.opensource.org/licenses/mit-license.php</url>
|
||||||
</license>
|
</license>
|
||||||
</licenses>
|
</licenses>
|
||||||
<scm>
|
<scm>
|
||||||
<url>git@github.com:bennidi/mbassador.git</url>
|
<url>git@github.com:bennidi/mbassador.git</url>
|
||||||
<connection>scm:git:git@github.com:bennidi/mbassador.git</connection>
|
<connection>scm:git:git@github.com:bennidi/mbassador.git</connection>
|
||||||
<tag>mbassador-1.1.4</tag>
|
<tag>mbassador-1.1.4</tag>
|
||||||
<developerConnection>scm:git:git@github.com:bennidi/mbassador.git</developerConnection>
|
<developerConnection>scm:git:git@github.com:bennidi/mbassador.git</developerConnection>
|
||||||
</scm>
|
</scm>
|
||||||
<developers>
|
<developers>
|
||||||
|
|
||||||
<developer>
|
<developer>
|
||||||
<id>bennidi</id>
|
<id>bennidi</id>
|
||||||
<name>Benjamin Diedrichsen</name>
|
<name>Benjamin Diedrichsen</name>
|
||||||
<timezone>+1</timezone>
|
<timezone>+1</timezone>
|
||||||
<email>b.diedrichsen@googlemail.com</email>
|
<email>b.diedrichsen@googlemail.com</email>
|
||||||
</developer>
|
</developer>
|
||||||
</developers>
|
</developers>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<nazgul-codestyle.version>2.0.1</nazgul-codestyle.version>
|
<nazgul-codestyle.version>2.0.1</nazgul-codestyle.version>
|
||||||
<jdk.version>1.6</jdk.version>
|
<jdk.version>1.6</jdk.version>
|
||||||
<pmd.plugin.version>3.0.1</pmd.plugin.version>
|
<pmd.plugin.version>3.0.1</pmd.plugin.version>
|
||||||
|
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<project.build.java.version>1.6</project.build.java.version>
|
<project.build.java.version>1.6</project.build.java.version>
|
||||||
<github.url>file://${project.basedir}/mvn-local-repo</github.url>
|
<github.url>file://${project.basedir}/mvn-local-repo</github.url>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
<artifactId>junit</artifactId>
|
<artifactId>junit</artifactId>
|
||||||
<version>4.10</version>
|
<version>4.10</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
<artifactId>slf4j-api</artifactId>
|
<artifactId>slf4j-api</artifactId>
|
||||||
<version>1.7.5</version>
|
<version>1.7.5</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
<artifactId>slf4j-log4j12</artifactId>
|
<artifactId>slf4j-log4j12</artifactId>
|
||||||
<version>1.7.5</version>
|
<version>1.7.5</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
<dependency>
|
||||||
|
<groupId>javax.el</groupId>
|
||||||
<!-- Local repository (for testing)
|
<artifactId>el-api</artifactId>
|
||||||
<distributionManagement>
|
<version>2.2</version>
|
||||||
<repository>
|
</dependency>
|
||||||
<id>mbassador-github-repo</id>
|
<dependency>
|
||||||
<url>${github.url}</url>
|
<groupId>de.odysseus.juel</groupId>
|
||||||
</repository>
|
<artifactId>juel-impl</artifactId>
|
||||||
</distributionManagement>
|
<version>2.2.7</version>
|
||||||
-->
|
<scope>runtime</scope>
|
||||||
|
<optional>true</optional>
|
||||||
<build>
|
</dependency>
|
||||||
<plugins>
|
<dependency>
|
||||||
<!-- plugin>
|
<groupId>de.odysseus.juel</groupId>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<artifactId>juel-spi</artifactId>
|
||||||
<artifactId>maven-pmd-plugin</artifactId>
|
<version>2.2.7</version>
|
||||||
<version>${pmd.plugin.version}</version>
|
<scope>runtime</scope>
|
||||||
<configuration>
|
<optional>true</optional>
|
||||||
<excludeRoots>
|
</dependency>
|
||||||
<excludeRoot>src/main/generated</excludeRoot>
|
</dependencies>
|
||||||
<excludeRoot>src/test</excludeRoot>
|
|
||||||
</excludeRoots>
|
<!-- Local repository (for testing)
|
||||||
<rulesets>
|
<distributionManagement>
|
||||||
<ruleset>/codestyle/pmd-rules.xml</ruleset>
|
<repository>
|
||||||
</rulesets>
|
<id>mbassador-github-repo</id>
|
||||||
<targetJdk>${jdk.version}</targetJdk>
|
<url>${github.url}</url>
|
||||||
<sourceEncoding>${project.build.sourceEncoding}</sourceEncoding>
|
</repository>
|
||||||
</configuration>
|
</distributionManagement>
|
||||||
<executions>
|
-->
|
||||||
<execution>
|
|
||||||
<goals>
|
<build>
|
||||||
<goal>check</goal>
|
<plugins>
|
||||||
<goal>cpd-check</goal>
|
<!-- plugin>
|
||||||
</goals>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
</execution>
|
<artifactId>maven-pmd-plugin</artifactId>
|
||||||
</executions>
|
<version>${pmd.plugin.version}</version>
|
||||||
<dependencies>
|
<configuration>
|
||||||
<dependency>
|
<excludeRoots>
|
||||||
<groupId>se.jguru.nazgul.tools.codestyle</groupId>
|
<excludeRoot>src/main/generated</excludeRoot>
|
||||||
<artifactId>nazgul-codestyle</artifactId>
|
<excludeRoot>src/test</excludeRoot>
|
||||||
<version>${nazgul-codestyle.version}</version>
|
</excludeRoots>
|
||||||
</dependency>
|
<rulesets>
|
||||||
</dependencies>
|
<ruleset>/codestyle/pmd-rules.xml</ruleset>
|
||||||
</plugin -->
|
</rulesets>
|
||||||
|
<targetJdk>${jdk.version}</targetJdk>
|
||||||
<plugin>
|
<sourceEncoding>${project.build.sourceEncoding}</sourceEncoding>
|
||||||
<groupId>org.apache.felix</groupId>
|
</configuration>
|
||||||
<artifactId>maven-bundle-plugin</artifactId>
|
<executions>
|
||||||
<version>2.3.7</version>
|
<execution>
|
||||||
<extensions>true</extensions>
|
<goals>
|
||||||
<configuration>
|
<goal>check</goal>
|
||||||
<instructions>
|
<goal>cpd-check</goal>
|
||||||
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
|
</goals>
|
||||||
<Export-Package>{local-packages}</Export-Package>
|
</execution>
|
||||||
</instructions>
|
</executions>
|
||||||
</configuration>
|
<dependencies>
|
||||||
</plugin>
|
<dependency>
|
||||||
|
<groupId>se.jguru.nazgul.tools.codestyle</groupId>
|
||||||
<plugin>
|
<artifactId>nazgul-codestyle</artifactId>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<version>${nazgul-codestyle.version}</version>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
</dependency>
|
||||||
<configuration>
|
</dependencies>
|
||||||
<source>${project.build.java.version}</source>
|
</plugin -->
|
||||||
<target>${project.build.java.version}</target>
|
|
||||||
</configuration>
|
<plugin>
|
||||||
</plugin>
|
<groupId>org.apache.felix</groupId>
|
||||||
|
<artifactId>maven-bundle-plugin</artifactId>
|
||||||
<plugin>
|
<version>2.3.7</version>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<extensions>true</extensions>
|
||||||
<artifactId>maven-release-plugin</artifactId>
|
<configuration>
|
||||||
<version>2.4</version>
|
<instructions>
|
||||||
<configuration>
|
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
|
||||||
<mavenExecutorId>forked-path</mavenExecutorId>
|
<Export-Package>{local-packages}</Export-Package>
|
||||||
</configuration>
|
</instructions>
|
||||||
</plugin>
|
</configuration>
|
||||||
|
</plugin>
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<plugin>
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<configuration>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
<skipTests>false</skipTests>
|
<configuration>
|
||||||
<excludes>
|
<source>${project.build.java.version}</source>
|
||||||
<!-- exclude the suite which is a convenience class for running all tests from IDE or using scripts -->
|
<target>${project.build.java.version}</target>
|
||||||
<exclude>AllTests.java</exclude>
|
</configuration>
|
||||||
</excludes>
|
</plugin>
|
||||||
</configuration>
|
|
||||||
</plugin>
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<!-- bind the source attaching to package phase -->
|
<artifactId>maven-release-plugin</artifactId>
|
||||||
<plugin>
|
<version>2.4</version>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<configuration>
|
||||||
<artifactId>maven-source-plugin</artifactId>
|
<mavenExecutorId>forked-path</mavenExecutorId>
|
||||||
<executions>
|
</configuration>
|
||||||
<execution>
|
</plugin>
|
||||||
<id>attach-sources</id>
|
|
||||||
<goals>
|
<plugin>
|
||||||
<goal>jar</goal>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
</goals>
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
</execution>
|
<configuration>
|
||||||
</executions>
|
<skipTests>false</skipTests>
|
||||||
</plugin>
|
<excludes>
|
||||||
|
<!-- exclude the suite which is a convenience class for running all tests from IDE or using scripts -->
|
||||||
<plugin>
|
<exclude>AllTests.java</exclude>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
</excludes>
|
||||||
<artifactId>maven-javadoc-plugin</artifactId>
|
</configuration>
|
||||||
<executions>
|
</plugin>
|
||||||
<execution>
|
|
||||||
<id>attach-javadocs</id>
|
<!-- bind the source attaching to package phase -->
|
||||||
<goals>
|
<plugin>
|
||||||
<goal>jar</goal>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
</goals>
|
<artifactId>maven-source-plugin</artifactId>
|
||||||
</execution>
|
<executions>
|
||||||
</executions>
|
<execution>
|
||||||
</plugin>
|
<id>attach-sources</id>
|
||||||
|
<goals>
|
||||||
<!--
|
<goal>jar</goal>
|
||||||
These two plugins take care of building and publishing the javadoc, using
|
</goals>
|
||||||
mvn clean javadoc:javadoc scm-publish:publish-scm
|
</execution>
|
||||||
-->
|
</executions>
|
||||||
<plugin>
|
</plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-javadoc-plugin</artifactId>
|
<plugin>
|
||||||
<version>2.9.1</version>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<configuration>
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
<aggregate>true</aggregate>
|
<executions>
|
||||||
<show>public</show>
|
<execution>
|
||||||
<nohelp>true</nohelp>
|
<id>attach-javadocs</id>
|
||||||
<header>mbassador, ${project.version}</header>
|
<goals>
|
||||||
<footer>mbassador, ${project.version}</footer>
|
<goal>jar</goal>
|
||||||
<doctitle>mbassador, ${project.version}</doctitle>
|
</goals>
|
||||||
</configuration>
|
</execution>
|
||||||
</plugin>
|
</executions>
|
||||||
<plugin>
|
</plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-scm-publish-plugin</artifactId>
|
<!--
|
||||||
<version>1.0-beta-2</version>
|
These two plugins take care of building and publishing the javadoc, using
|
||||||
<configuration>
|
mvn clean javadoc:javadoc scm-publish:publish-scm
|
||||||
<checkoutDirectory>${project.build.directory}/scmpublish</checkoutDirectory>
|
-->
|
||||||
<checkinComment>Publishing javadoc for ${project.artifactId}:${project.version}</checkinComment>
|
<plugin>
|
||||||
<content>${project.reporting.outputDirectory}/apidocs</content>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<skipDeletedFiles>true</skipDeletedFiles>
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
<pubScmUrl>scm:git:git@github.com:bennidi/mbassador.git</pubScmUrl>
|
<version>2.9.1</version>
|
||||||
<scmBranch>gh-pages</scmBranch> <!-- branch with static site on github-->
|
<configuration>
|
||||||
</configuration>
|
<aggregate>true</aggregate>
|
||||||
</plugin>
|
<show>public</show>
|
||||||
</plugins>
|
<nohelp>true</nohelp>
|
||||||
</build>
|
<header>mbassador, ${project.version}</header>
|
||||||
|
<footer>mbassador, ${project.version}</footer>
|
||||||
<profiles>
|
<doctitle>mbassador, ${project.version}</doctitle>
|
||||||
<profile>
|
</configuration>
|
||||||
<id>release-sign-artifacts</id>
|
</plugin>
|
||||||
<activation>
|
<plugin>
|
||||||
<property>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<name>performRelease</name>
|
<artifactId>maven-scm-publish-plugin</artifactId>
|
||||||
<value>true</value>
|
<version>1.0-beta-2</version>
|
||||||
</property>
|
<configuration>
|
||||||
</activation>
|
<checkoutDirectory>${project.build.directory}/scmpublish</checkoutDirectory>
|
||||||
<build>
|
<checkinComment>Publishing javadoc for ${project.artifactId}:${project.version}</checkinComment>
|
||||||
<plugins>
|
<content>${project.reporting.outputDirectory}/apidocs</content>
|
||||||
<plugin>
|
<skipDeletedFiles>true</skipDeletedFiles>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<pubScmUrl>scm:git:git@github.com:bennidi/mbassador.git</pubScmUrl>
|
||||||
<artifactId>maven-gpg-plugin</artifactId>
|
<scmBranch>gh-pages</scmBranch> <!-- branch with static site on github-->
|
||||||
<version>1.4</version>
|
</configuration>
|
||||||
<executions>
|
</plugin>
|
||||||
<execution>
|
</plugins>
|
||||||
<id>sign-artifacts</id>
|
</build>
|
||||||
<phase>verify</phase>
|
|
||||||
<goals>
|
<profiles>
|
||||||
<goal>sign</goal>
|
<profile>
|
||||||
</goals>
|
<id>release-sign-artifacts</id>
|
||||||
</execution>
|
<activation>
|
||||||
</executions>
|
<property>
|
||||||
</plugin>
|
<name>performRelease</name>
|
||||||
</plugins>
|
<value>true</value>
|
||||||
</build>
|
</property>
|
||||||
</profile>
|
</activation>
|
||||||
</profiles>
|
<build>
|
||||||
</project>
|
<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;
|
package net.engio.mbassy.dispatch;
|
||||||
|
|
||||||
import net.engio.mbassy.bus.MessagePublication;
|
import net.engio.mbassy.bus.MessagePublication;
|
||||||
import net.engio.mbassy.listener.IMessageFilter;
|
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
|
* A dispatcher that implements message filtering based on the filter configuration
|
||||||
* message dispatcher after having performed the filtering logic.
|
* 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
|
* @author bennidi
|
||||||
*/
|
* Date: 11/23/12
|
||||||
public class FilteredMessageDispatcher extends DelegatingMessageDispatcher {
|
*/
|
||||||
|
public class FilteredMessageDispatcher extends DelegatingMessageDispatcher {
|
||||||
private final IMessageFilter[] filter;
|
|
||||||
|
private final IMessageFilter[] filter;
|
||||||
public FilteredMessageDispatcher(IMessageDispatcher dispatcher) {
|
|
||||||
super(dispatcher);
|
public FilteredMessageDispatcher(IMessageDispatcher dispatcher) {
|
||||||
this.filter = dispatcher.getContext().getHandlerMetadata().getFilter();
|
super(dispatcher);
|
||||||
}
|
this.filter = dispatcher.getContext().getHandlerMetadata().getFilter();
|
||||||
|
}
|
||||||
private boolean passesFilter(Object message) {
|
|
||||||
|
private boolean passesFilter(Object message) {
|
||||||
if (filter == null) {
|
|
||||||
return true;
|
if (filter == null) {
|
||||||
} else {
|
return true;
|
||||||
for (IMessageFilter aFilter : filter) {
|
} else {
|
||||||
if (!aFilter.accepts(message, getContext().getHandlerMetadata())) {
|
for (IMessageFilter aFilter : filter) {
|
||||||
return false;
|
if (!aFilter.accepts(message, getContext().getHandlerMetadata())) {
|
||||||
}
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
}
|
||||||
}
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void dispatch(MessagePublication publication, Object message, Iterable listeners){
|
@Override
|
||||||
if (passesFilter(message)) {
|
public void dispatch(MessagePublication publication, Object message, Iterable listeners){
|
||||||
getDelegate().dispatch(publication, message, 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;
|
package net.engio.mbassy.listener;
|
||||||
|
|
||||||
import net.engio.mbassy.dispatch.HandlerInvocation;
|
import net.engio.mbassy.dispatch.HandlerInvocation;
|
||||||
import net.engio.mbassy.dispatch.ReflectiveHandlerInvocation;
|
import net.engio.mbassy.dispatch.ReflectiveHandlerInvocation;
|
||||||
|
|
||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark any method of any class(=listener) as a message handler and configure the handler
|
* Mark any method of any class(=listener) as a message handler and configure the handler
|
||||||
* using different properties.
|
* using different properties.
|
||||||
*
|
*
|
||||||
* @author bennidi
|
* @author bennidi
|
||||||
* Date: 2/8/12
|
* Date: 2/8/12
|
||||||
*/
|
*/
|
||||||
@Retention(value = RetentionPolicy.RUNTIME)
|
@Retention(value = RetentionPolicy.RUNTIME)
|
||||||
@Inherited
|
@Inherited
|
||||||
@Target(value = {ElementType.METHOD})
|
@Target(value = {ElementType.METHOD})
|
||||||
public @interface Handler {
|
public @interface Handler {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add any numbers of filters to the handler. All filters are evaluated before the 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.
|
* is actually invoked, which is only if all the filters accept the message.
|
||||||
*/
|
*/
|
||||||
Filter[] filters() default {};
|
Filter[] filters() default {};
|
||||||
|
|
||||||
/**
|
|
||||||
* Define the mode in which a message is delivered to each listener. Listeners can be notified
|
/**
|
||||||
* sequentially or concurrently.
|
* 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
|
||||||
Invoke delivery() default Invoke.Synchronously;
|
* <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.
|
||||||
* 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
|
String condition() default "";
|
||||||
* the same message type are invoked.
|
|
||||||
*/
|
/**
|
||||||
int priority() default 0;
|
* Define the mode in which a message is delivered to each listener. Listeners can be notified
|
||||||
|
* sequentially or concurrently.
|
||||||
/**
|
*/
|
||||||
* Define whether or not the handler accepts sub types of the message type it declares in its
|
Invoke delivery() default Invoke.Synchronously;
|
||||||
* signature.
|
|
||||||
*/
|
/**
|
||||||
boolean rejectSubtypes() default false;
|
* 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.
|
||||||
/**
|
*/
|
||||||
* Enable or disable the handler. Disabled handlers do not receive any messages.
|
int priority() default 0;
|
||||||
* 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
|
/**
|
||||||
*/
|
* Define whether or not the handler accepts sub types of the message type it declares in its
|
||||||
boolean enabled() default true;
|
* signature.
|
||||||
|
*/
|
||||||
|
boolean rejectSubtypes() default false;
|
||||||
/**
|
|
||||||
* 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.
|
* 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
|
||||||
* Note: Providing a custom invocation will most likely reduce performance, since the JIT-Compiler
|
* handlers that have been declared by a superclass but do not apply to the subclass
|
||||||
* can not do some of its sophisticated byte code optimizations.
|
*/
|
||||||
*
|
boolean enabled() default true;
|
||||||
*/
|
|
||||||
Class<? extends HandlerInvocation> invocation() default ReflectiveHandlerInvocation.class;
|
|
||||||
|
/**
|
||||||
|
* 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;
|
package net.engio.mbassy.listener;
|
||||||
|
|
||||||
import net.engio.mbassy.dispatch.HandlerInvocation;
|
import net.engio.mbassy.dispatch.HandlerInvocation;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Any method in any class annotated with the @Handler annotation represents a message handler. The class that contains
|
* 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
|
* 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.
|
* defines such a message listener.
|
||||||
*
|
*
|
||||||
* @author bennidi
|
* @author bennidi
|
||||||
* Date: 11/14/12
|
* Date: 11/14/12
|
||||||
*/
|
*/
|
||||||
public class MessageHandler {
|
public class MessageHandler {
|
||||||
|
|
||||||
public static final class Properties{
|
public static final class Properties{
|
||||||
|
|
||||||
public static final String HandlerMethod = "handler";
|
public static final String HandlerMethod = "handler";
|
||||||
public static final String InvocationMode = "invocationMode";
|
public static final String InvocationMode = "invocationMode";
|
||||||
public static final String Filter = "filter";
|
public static final String Filter = "filter";
|
||||||
public static final String Enveloped = "envelope";
|
public static final String Condition = "condition";
|
||||||
public static final String HandledMessages = "messages";
|
public static final String Enveloped = "envelope";
|
||||||
public static final String IsSynchronized = "synchronized";
|
public static final String HandledMessages = "messages";
|
||||||
public static final String Listener = "listener";
|
public static final String IsSynchronized = "synchronized";
|
||||||
public static final String AcceptSubtypes = "subtypes";
|
public static final String Listener = "listener";
|
||||||
public static final String Priority = "priority";
|
public static final String AcceptSubtypes = "subtypes";
|
||||||
public static final String Invocation = "invocation";
|
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.
|
/**
|
||||||
*
|
* 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 handler The handler annotated method of the listener
|
||||||
* @param filter The set of preconfigured filters if any
|
* @param handlerConfig The annotation that configures the handler
|
||||||
* @param listenerConfig The listener metadata
|
* @param filter The set of preconfigured filters if any
|
||||||
* @return A map of properties initialized from the given parameters that will conform to the requirements of the
|
* @param listenerConfig The listener metadata
|
||||||
* {@link MessageHandler} constructor. See {@see MessageHandler.validate()} for more details.
|
* @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){
|
public static final Map<String, Object> Create(Method handler, Handler handlerConfig, IMessageFilter[] filter, MessageListener listenerConfig){
|
||||||
throw new IllegalArgumentException("The message handler configuration may not be null");
|
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
|
net.engio.mbassy.listener.Enveloped enveloped = handler.getAnnotation(Enveloped.class);
|
||||||
? enveloped.messages()
|
Class[] handledMessages = enveloped != null
|
||||||
: handler.getParameterTypes();
|
? enveloped.messages()
|
||||||
handler.setAccessible(true);
|
: handler.getParameterTypes();
|
||||||
Map<String, Object> properties = new HashMap<String, Object>();
|
handler.setAccessible(true);
|
||||||
properties.put(HandlerMethod, handler);
|
Map<String, Object> properties = new HashMap<String, Object>();
|
||||||
properties.put(Filter, filter != null ? filter : new IMessageFilter[]{});
|
properties.put(HandlerMethod, handler);
|
||||||
properties.put(Priority, handlerConfig.priority());
|
properties.put(Filter, filter != null ? filter : new IMessageFilter[]{});
|
||||||
properties.put(Invocation, handlerConfig.invocation());
|
properties.put(Condition, handlerConfig.condition());
|
||||||
properties.put(InvocationMode, handlerConfig.delivery());
|
properties.put(Priority, handlerConfig.priority());
|
||||||
properties.put(Enveloped, enveloped != null);
|
properties.put(Invocation, handlerConfig.invocation());
|
||||||
properties.put(AcceptSubtypes, !handlerConfig.rejectSubtypes());
|
properties.put(InvocationMode, handlerConfig.delivery());
|
||||||
properties.put(Listener, listenerConfig);
|
properties.put(Enveloped, enveloped != null);
|
||||||
properties.put(IsSynchronized, handler.getAnnotation(Synchronized.class) != null);
|
properties.put(AcceptSubtypes, !handlerConfig.rejectSubtypes());
|
||||||
properties.put(HandledMessages, handledMessages);
|
properties.put(Listener, listenerConfig);
|
||||||
return properties;
|
properties.put(IsSynchronized, handler.getAnnotation(Synchronized.class) != null);
|
||||||
}
|
properties.put(HandledMessages, handledMessages);
|
||||||
}
|
return properties;
|
||||||
|
}
|
||||||
|
}
|
||||||
private final Method handler;
|
|
||||||
|
|
||||||
private final IMessageFilter[] filter;
|
private final Method handler;
|
||||||
|
|
||||||
private final int priority;
|
private final IMessageFilter[] filter;
|
||||||
|
|
||||||
private final Class<? extends HandlerInvocation> invocation;
|
private String condition;
|
||||||
|
|
||||||
private final Invoke invocationMode;
|
private final int priority;
|
||||||
|
|
||||||
private final boolean isEnvelope;
|
private final Class<? extends HandlerInvocation> invocation;
|
||||||
|
|
||||||
private final Class[] handledMessages;
|
private final Invoke invocationMode;
|
||||||
|
|
||||||
private final boolean acceptsSubtypes;
|
private final boolean isEnvelope;
|
||||||
|
|
||||||
private final MessageListener listenerConfig;
|
private final Class[] handledMessages;
|
||||||
|
|
||||||
private final boolean isSynchronized;
|
private final boolean acceptsSubtypes;
|
||||||
|
|
||||||
public MessageHandler(Map<String, Object> properties){
|
private final MessageListener listenerConfig;
|
||||||
super();
|
|
||||||
validate(properties);
|
private final boolean isSynchronized;
|
||||||
this.handler = (Method)properties.get(Properties.HandlerMethod);
|
|
||||||
this.filter = (IMessageFilter[])properties.get(Properties.Filter);
|
|
||||||
this.priority = (Integer)properties.get(Properties.Priority);
|
public MessageHandler(Map<String, Object> properties){
|
||||||
this.invocation = (Class<? extends HandlerInvocation>)properties.get(Properties.Invocation);
|
super();
|
||||||
this.invocationMode = (Invoke)properties.get(Properties.InvocationMode);
|
validate(properties);
|
||||||
this.isEnvelope = (Boolean)properties.get(Properties.Enveloped);
|
this.handler = (Method)properties.get(Properties.HandlerMethod);
|
||||||
this.acceptsSubtypes = (Boolean)properties.get(Properties.AcceptSubtypes);
|
this.filter = (IMessageFilter[])properties.get(Properties.Filter);
|
||||||
this.listenerConfig = (MessageListener)properties.get(Properties.Listener);
|
this.condition = (String)properties.get(Properties.Condition);
|
||||||
this.isSynchronized = (Boolean)properties.get(Properties.IsSynchronized);
|
this.priority = (Integer)properties.get(Properties.Priority);
|
||||||
this.handledMessages = (Class[])properties.get(Properties.HandledMessages);
|
this.invocation = (Class<? extends HandlerInvocation>)properties.get(Properties.Invocation);
|
||||||
}
|
this.invocationMode = (Invoke)properties.get(Properties.InvocationMode);
|
||||||
|
this.isEnvelope = (Boolean)properties.get(Properties.Enveloped);
|
||||||
private void validate(Map<String, Object> properties){
|
this.acceptsSubtypes = (Boolean)properties.get(Properties.AcceptSubtypes);
|
||||||
Object[][] expectedProperties = new Object[][]{
|
this.listenerConfig = (MessageListener)properties.get(Properties.Listener);
|
||||||
new Object[]{Properties.HandlerMethod, Method.class },
|
this.isSynchronized = (Boolean)properties.get(Properties.IsSynchronized);
|
||||||
new Object[]{Properties.Priority, Integer.class },
|
this.handledMessages = (Class[])properties.get(Properties.HandledMessages);
|
||||||
new Object[]{Properties.Invocation, Class.class },
|
}
|
||||||
new Object[]{Properties.Filter, IMessageFilter[].class },
|
|
||||||
new Object[]{Properties.Enveloped, Boolean.class },
|
private void validate(Map<String, Object> properties){
|
||||||
new Object[]{Properties.HandledMessages, Class[].class },
|
Object[][] expectedProperties = new Object[][]{
|
||||||
new Object[]{Properties.IsSynchronized, Boolean.class },
|
new Object[]{Properties.HandlerMethod, Method.class },
|
||||||
new Object[]{Properties.Listener, MessageListener.class },
|
new Object[]{Properties.Priority, Integer.class },
|
||||||
new Object[]{Properties.AcceptSubtypes, Boolean.class }
|
new Object[]{Properties.Invocation, Class.class },
|
||||||
};
|
new Object[]{Properties.Filter, IMessageFilter[].class },
|
||||||
for(Object[] property : expectedProperties){
|
new Object[]{Properties.Condition, String.class },
|
||||||
if (properties.get(property[0]) == null || !((Class)property[1]).isAssignableFrom(properties.get(property[0]).getClass()))
|
new Object[]{Properties.Enveloped, Boolean.class },
|
||||||
throw new IllegalArgumentException("Property " + property[0] + " was expected to be not null and of type " + property[1]
|
new Object[]{Properties.HandledMessages, Class[].class },
|
||||||
+ " but was: " + properties.get(property[0]));
|
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()))
|
||||||
public boolean isSynchronized(){
|
throw new IllegalArgumentException("Property " + property[0] + " was expected to be not null and of type " + property[1]
|
||||||
return isSynchronized;
|
+ " but was: " + properties.get(property[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean useStrongReferences(){
|
|
||||||
return listenerConfig.useStrongReferences();
|
}
|
||||||
}
|
|
||||||
|
public boolean isSynchronized(){
|
||||||
public boolean isFromListener(Class listener){
|
return isSynchronized;
|
||||||
return listenerConfig.isFromListener(listener);
|
}
|
||||||
}
|
|
||||||
|
public boolean useStrongReferences(){
|
||||||
public boolean isAsynchronous() {
|
return listenerConfig.useStrongReferences();
|
||||||
return invocationMode.equals(Invoke.Asynchronously);
|
}
|
||||||
}
|
|
||||||
|
public boolean isFromListener(Class listener){
|
||||||
public boolean isFiltered() {
|
return listenerConfig.isFromListener(listener);
|
||||||
return filter.length > 0;
|
}
|
||||||
}
|
|
||||||
|
public boolean isAsynchronous() {
|
||||||
public int getPriority() {
|
return invocationMode.equals(Invoke.Asynchronously);
|
||||||
return priority;
|
}
|
||||||
}
|
|
||||||
|
public boolean isFiltered() {
|
||||||
public Method getHandler() {
|
return filter.length > 0 || (condition != null && condition.trim().length() > 0);
|
||||||
return handler;
|
}
|
||||||
}
|
|
||||||
|
public int getPriority() {
|
||||||
public IMessageFilter[] getFilter() {
|
return priority;
|
||||||
return filter;
|
}
|
||||||
}
|
|
||||||
|
public Method getHandler() {
|
||||||
public Class[] getHandledMessages() {
|
return handler;
|
||||||
return handledMessages;
|
}
|
||||||
}
|
|
||||||
|
public IMessageFilter[] getFilter() {
|
||||||
public boolean isEnveloped() {
|
return filter;
|
||||||
return isEnvelope;
|
}
|
||||||
}
|
|
||||||
|
public String getCondition() {
|
||||||
public Class<? extends HandlerInvocation> getHandlerInvocation(){
|
return this.condition;
|
||||||
return invocation;
|
}
|
||||||
}
|
|
||||||
|
public Class[] getHandledMessages() {
|
||||||
public boolean handlesMessage(Class<?> messageType) {
|
return handledMessages;
|
||||||
for (Class<?> handledMessage : handledMessages) {
|
}
|
||||||
if (handledMessage.equals(messageType)) {
|
|
||||||
return true;
|
public boolean isEnveloped() {
|
||||||
}
|
return isEnvelope;
|
||||||
if (handledMessage.isAssignableFrom(messageType) && acceptsSubtypes()) {
|
}
|
||||||
return true;
|
|
||||||
}
|
public Class<? extends HandlerInvocation> getHandlerInvocation(){
|
||||||
}
|
return invocation;
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
|
public boolean handlesMessage(Class<?> messageType) {
|
||||||
public boolean acceptsSubtypes() {
|
for (Class<?> handledMessage : handledMessages) {
|
||||||
return acceptsSubtypes;
|
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