Updated the names of the projects
This commit is contained in:
@@ -0,0 +1 @@
|
||||
http\://www.springframework.org/schema/integration=org.springframework.integration.config.IntegrationNamespaceHandler
|
||||
@@ -0,0 +1 @@
|
||||
http\://www.springframework.org/schema/integration/spring-integration-core-1.0.xsd=org/springframework/integration/config/spring-integration-core-1.0.xsd
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration;
|
||||
|
||||
/**
|
||||
* Exception that indicates an incorrectly configured integration component.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class ConfigurationException extends RuntimeException {
|
||||
|
||||
public ConfigurationException(String description) {
|
||||
super(description);
|
||||
}
|
||||
|
||||
public ConfigurationException(String description, Throwable cause) {
|
||||
super(description, cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.integration.router.AggregatingMessageHandler;
|
||||
|
||||
/**
|
||||
* Indicates that a method is capable of aggregating messages.
|
||||
*
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Handler
|
||||
public @interface Aggregator {
|
||||
|
||||
String defaultReplyChannel() default "";
|
||||
|
||||
String discardChannel() default "";
|
||||
|
||||
long sendTimeout() default AggregatingMessageHandler.DEFAULT_SEND_TIMEOUT;
|
||||
|
||||
long timeout() default AggregatingMessageHandler.DEFAULT_TIMEOUT;
|
||||
|
||||
boolean sendPartialResultsOnTimeout() default false;
|
||||
|
||||
long reaperInterval() default AggregatingMessageHandler.DEFAULT_REAPER_INTERVAL;
|
||||
|
||||
int trackedCorrelationIdCapacity() default AggregatingMessageHandler.DEFAULT_TRACKED_CORRRELATION_ID_CAPACITY;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Indicates that a method is capable of asserting if a list of messages or
|
||||
* payload objects is complete.
|
||||
*
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
@Documented
|
||||
public @interface CompletionStrategy {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.integration.endpoint.ConcurrencyPolicy;
|
||||
|
||||
/**
|
||||
* Defines the {@link ConcurrencyPolicy} settings for a {@link MessageEndpoint @MessageEndpoint}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface Concurrency {
|
||||
|
||||
int coreSize() default ConcurrencyPolicy.DEFAULT_CORE_SIZE;
|
||||
|
||||
int maxSize() default ConcurrencyPolicy.DEFAULT_MAX_SIZE;
|
||||
|
||||
int queueCapacity() default ConcurrencyPolicy.DEFAULT_QUEUE_CAPACITY;
|
||||
|
||||
int keepAliveSeconds() default ConcurrencyPolicy.DEFAULT_KEEP_ALIVE_SECONDS;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
import org.springframework.integration.message.Message;
|
||||
|
||||
/**
|
||||
* Indicates that a method is capable of sending messages. The method must
|
||||
* accept a single parameter that is either a {@link Message} or an Object to
|
||||
* be passed as a message payload. The enclosing class should be annotated with
|
||||
* {@link MessageEndpoint @MessageEndpoint}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
@java.lang.annotation.Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface DefaultOutput {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
||||
/**
|
||||
* Indicates that a method is capable of handling a message or message payload.
|
||||
* The method may only accept a single parameter, and the enclosing class should
|
||||
* be annotated with {@link MessageEndpoint @MessageEndpoint}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface Handler {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Indicates that a class is capable of serving as a message endpoint.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
@Component
|
||||
public @interface MessageEndpoint {
|
||||
|
||||
String input() default "";
|
||||
|
||||
String output() default "";
|
||||
|
||||
int pollPeriod() default 0;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.integration.scheduling.PollingSchedule;
|
||||
|
||||
/**
|
||||
* Annotation that can be specified at class-level alongside a
|
||||
* {@link MessageEndpoint @MessageEndpoint} annotation in order to provide the
|
||||
* scheduling information for that endpoint. Alternatively, as a method-level
|
||||
* annotation, this indicates that a method is capable of providing messages.
|
||||
* The method must not accept any parameters but can return either a single
|
||||
* object or collection. The enclosing class should be annotated with
|
||||
* {@link MessageEndpoint @MessageEndpoint}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface Polled {
|
||||
|
||||
int period() default 0;
|
||||
|
||||
long initialDelay() default PollingSchedule.DEFAULT_INITIAL_DELAY;
|
||||
|
||||
boolean fixedRate() default PollingSchedule.DEFAULT_FIXED_RATE;
|
||||
|
||||
TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Indicates that the method's return value should be published to the specified
|
||||
* channel. The value will only be published if non-null.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface Publisher {
|
||||
|
||||
String channel();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Indicates that a method is capable of resolving to a channel or channel name
|
||||
* based on a message, message payload, message attribute, or message property.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
@Handler
|
||||
public @interface Router {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Indicates that a method is capable of splitting a single message or message
|
||||
* payload to produce multiple messages or payloads.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Handler
|
||||
public @interface Splitter {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Indicates that a method-invoking handler adapter should delegate to this
|
||||
* method.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface Subscriber {
|
||||
|
||||
String channel();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.aop;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.integration.channel.ChannelRegistry;
|
||||
import org.springframework.integration.channel.MessageChannel;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link MessagePublishingInterceptor} that resolves the channel from the
|
||||
* publisher annotation of the invoked method.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class AnnotationAwareMessagePublishingInterceptor extends MessagePublishingInterceptor {
|
||||
|
||||
private Class<? extends Annotation> publisherAnnotationType;
|
||||
|
||||
private String channelAttributeName;
|
||||
|
||||
private ChannelRegistry channelRegistry;
|
||||
|
||||
|
||||
public AnnotationAwareMessagePublishingInterceptor(Class<? extends Annotation> publisherAnnotationType,
|
||||
String channelAttributeName, ChannelRegistry channelRegistry) {
|
||||
Assert.notNull(publisherAnnotationType, "'publisherAnnotationType' must not be null");
|
||||
Assert.notNull(channelAttributeName, "'channelAttributeName' must not be null");
|
||||
Assert.notNull(channelRegistry, "'channelRegistry' must not be null");
|
||||
this.publisherAnnotationType = publisherAnnotationType;
|
||||
this.channelAttributeName = channelAttributeName;
|
||||
this.channelRegistry = channelRegistry;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected MessageChannel resolveChannel(MethodInvocation invocation) {
|
||||
Class<?> targetClass = AopUtils.getTargetClass(invocation.getThis());
|
||||
Method method = AopUtils.getMostSpecificMethod(invocation.getMethod(), targetClass);
|
||||
Annotation annotation = AnnotationUtils.getAnnotation(method, this.publisherAnnotationType);
|
||||
if (annotation != null) {
|
||||
String channelName = (String) AnnotationUtils.getValue(annotation, this.channelAttributeName);
|
||||
if (channelName != null) {
|
||||
MessageChannel channel = this.channelRegistry.lookupChannel(channelName);
|
||||
if (channel != null) {
|
||||
return channel;
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.resolveChannel(invocation);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.aop;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.integration.channel.MessageChannel;
|
||||
import org.springframework.integration.message.GenericMessage;
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.message.MessageCreator;
|
||||
|
||||
/**
|
||||
* Interceptor that publishes a target method's return value to a channel.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class MessagePublishingInterceptor implements MethodInterceptor {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private volatile MessageCreator messageCreator;
|
||||
|
||||
private volatile MessageChannel defaultChannel;
|
||||
|
||||
|
||||
public void setDefaultChannel(MessageChannel defaultChannel) {
|
||||
this.defaultChannel = defaultChannel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the {@link MessageCreator} to use when creating a message from the
|
||||
* return value Object.
|
||||
*
|
||||
* @param messageCreator the MessageCreator to use
|
||||
*/
|
||||
public void setMessageCreator(MessageCreator messageCreator) {
|
||||
this.messageCreator = messageCreator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the target method and publish its return value.
|
||||
*/
|
||||
public Object invoke(MethodInvocation invocation) throws Throwable {
|
||||
Object retval = invocation.proceed();
|
||||
if (retval != null) {
|
||||
MessageChannel channel = this.resolveChannel(invocation);
|
||||
if (channel == null) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("unable to resolve channel for intercepted method '" +
|
||||
invocation.getMethod().getName() + "'");
|
||||
}
|
||||
}
|
||||
else {
|
||||
Message<?> message = (this.messageCreator != null) ? this.messageCreator.createMessage(retval) : new GenericMessage<Object>(retval);
|
||||
channel.send(message);
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses may override this method to provide custom behavior.
|
||||
*/
|
||||
protected MessageChannel resolveChannel(MethodInvocation invocation) {
|
||||
return this.defaultChannel;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.aop;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
|
||||
import org.springframework.aop.Pointcut;
|
||||
import org.springframework.aop.support.AbstractPointcutAdvisor;
|
||||
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
|
||||
import org.springframework.integration.annotation.Publisher;
|
||||
import org.springframework.integration.channel.ChannelRegistry;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Advisor whose pointcut matches a method annotation and whose advice will
|
||||
* publish a message to the channel provided by that annotation.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @see Publisher
|
||||
* @see AnnotationAwareMessagePublishingInterceptor
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class PublisherAnnotationAdvisor extends AbstractPointcutAdvisor {
|
||||
|
||||
private AnnotationAwareMessagePublishingInterceptor advice;
|
||||
|
||||
private AnnotationMatchingPointcut pointcut;
|
||||
|
||||
|
||||
public PublisherAnnotationAdvisor(ChannelRegistry channelRegistry) {
|
||||
this(Publisher.class, "channel", channelRegistry);
|
||||
}
|
||||
|
||||
public PublisherAnnotationAdvisor(Class<? extends Annotation> publisherAnnotationType, String channelNameAttribute,
|
||||
ChannelRegistry channelRegistry) {
|
||||
Assert.notNull(publisherAnnotationType, "'publisherAnnotationType' must not be null");
|
||||
Assert.notNull(channelNameAttribute, "'channelNameAttribute' must not be null");
|
||||
Assert.notNull(channelRegistry, "'channelRegistry' must not be null");
|
||||
this.pointcut = AnnotationMatchingPointcut.forMethodAnnotation(publisherAnnotationType);
|
||||
this.advice = new AnnotationAwareMessagePublishingInterceptor(publisherAnnotationType, channelNameAttribute,
|
||||
channelRegistry);
|
||||
}
|
||||
|
||||
|
||||
public Pointcut getPointcut() {
|
||||
return this.pointcut;
|
||||
}
|
||||
|
||||
public Advice getAdvice() {
|
||||
return this.advice;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.bus;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.integration.channel.MessageChannel;
|
||||
import org.springframework.integration.channel.RendezvousChannel;
|
||||
import org.springframework.integration.channel.interceptor.ChannelInterceptorAdapter;
|
||||
import org.springframework.integration.message.Message;
|
||||
|
||||
/**
|
||||
* The default error channel implementation used by the {@link MessageBus}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class DefaultErrorChannel extends RendezvousChannel {
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
|
||||
public DefaultErrorChannel() {
|
||||
this.addInterceptor(new ErrorLoggingInterceptor());
|
||||
}
|
||||
|
||||
|
||||
private class ErrorLoggingInterceptor extends ChannelInterceptorAdapter {
|
||||
|
||||
@Override
|
||||
public void postSend(Message<?> message, MessageChannel channel, boolean sent) {
|
||||
if (!sent) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Error channel rejected Message. Are any handlers subscribed? " + message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Even if the error channel has no subscribers, errors are at least visible at debug level.
|
||||
*/
|
||||
@Override
|
||||
public boolean preSend(Message<?> message, MessageChannel channel) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
String errorMessage = "Error Received. Message: " + message.toString();
|
||||
Object payload = message.getPayload();
|
||||
if (payload instanceof Throwable) {
|
||||
logger.debug(errorMessage, (Throwable) payload);
|
||||
}
|
||||
else {
|
||||
logger.debug(errorMessage);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,520 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.bus;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.context.event.ApplicationEventMulticaster;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.context.event.SimpleApplicationEventMulticaster;
|
||||
import org.springframework.context.support.AbstractApplicationContext;
|
||||
import org.springframework.integration.ConfigurationException;
|
||||
import org.springframework.integration.channel.ChannelRegistry;
|
||||
import org.springframework.integration.channel.ChannelRegistryAware;
|
||||
import org.springframework.integration.channel.DefaultChannelRegistry;
|
||||
import org.springframework.integration.channel.MessageChannel;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.channel.factory.ChannelFactory;
|
||||
import org.springframework.integration.channel.factory.QueueChannelFactory;
|
||||
import org.springframework.integration.endpoint.ConcurrencyPolicy;
|
||||
import org.springframework.integration.endpoint.DefaultEndpointRegistry;
|
||||
import org.springframework.integration.endpoint.EndpointRegistry;
|
||||
import org.springframework.integration.endpoint.HandlerEndpoint;
|
||||
import org.springframework.integration.endpoint.MessageEndpoint;
|
||||
import org.springframework.integration.endpoint.SourceEndpoint;
|
||||
import org.springframework.integration.endpoint.TargetEndpoint;
|
||||
import org.springframework.integration.handler.MessageHandler;
|
||||
import org.springframework.integration.message.Target;
|
||||
import org.springframework.integration.scheduling.MessagePublishingErrorHandler;
|
||||
import org.springframework.integration.scheduling.MessagingTask;
|
||||
import org.springframework.integration.scheduling.MessagingTaskScheduler;
|
||||
import org.springframework.integration.scheduling.Schedule;
|
||||
import org.springframework.integration.scheduling.SimpleMessagingTaskScheduler;
|
||||
import org.springframework.integration.scheduling.Subscription;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* The messaging bus. Serves as a registry for channels and endpoints, manages their lifecycle,
|
||||
* and activates subscriptions.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
public class MessageBus implements ChannelRegistry, EndpointRegistry, ApplicationContextAware, ApplicationListener, Lifecycle {
|
||||
|
||||
public static final String ERROR_CHANNEL_NAME = "errorChannel";
|
||||
|
||||
private static final int DEFAULT_DISPATCHER_POOL_SIZE = 10;
|
||||
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
private volatile ChannelFactory channelFactory = new QueueChannelFactory();
|
||||
|
||||
private final ChannelRegistry channelRegistry = new DefaultChannelRegistry();
|
||||
|
||||
private final EndpointRegistry endpointRegistry = new DefaultEndpointRegistry();
|
||||
|
||||
private final Map<MessageChannel, SubscriptionManager> subscriptionManagers = new ConcurrentHashMap<MessageChannel, SubscriptionManager>();
|
||||
|
||||
private final List<Lifecycle> lifecycleEndpoints = new CopyOnWriteArrayList<Lifecycle>();
|
||||
|
||||
private volatile MessagingTaskScheduler taskScheduler;
|
||||
|
||||
private volatile ScheduledExecutorService executor;
|
||||
|
||||
private volatile ConcurrencyPolicy defaultConcurrencyPolicy;
|
||||
|
||||
private volatile boolean configureAsyncEventMulticaster = false;
|
||||
|
||||
private volatile boolean autoCreateChannels = false;
|
||||
|
||||
private volatile boolean autoStartup = true;
|
||||
|
||||
private volatile boolean initialized;
|
||||
|
||||
private volatile boolean initializing;
|
||||
|
||||
private volatile boolean starting;
|
||||
|
||||
private volatile boolean running;
|
||||
|
||||
private final Object lifecycleMonitor = new Object();
|
||||
|
||||
|
||||
/**
|
||||
* Set the {@link ChannelFactory} to use for auto-creating channels.
|
||||
*/
|
||||
public void setChannelFactory(ChannelFactory channelFactory) {
|
||||
this.channelFactory = channelFactory;
|
||||
}
|
||||
|
||||
public ChannelFactory getChannelFactory() {
|
||||
return channelFactory;
|
||||
}
|
||||
|
||||
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
Assert.notNull(applicationContext, "'applicationContext' must not be null");
|
||||
if (applicationContext.getBeanNamesForType(this.getClass()).length > 1) {
|
||||
throw new ConfigurationException("Only one instance of '" + this.getClass().getSimpleName()
|
||||
+ "' is allowed per ApplicationContext.");
|
||||
}
|
||||
this.registerChannels(applicationContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link ScheduledExecutorService} to use for scheduling message dispatchers.
|
||||
*/
|
||||
public void setScheduledExecutorService(ScheduledExecutorService executor) {
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the default concurrency policy to be used for any endpoint that
|
||||
* is registered without an explicitly provided policy of its own.
|
||||
*/
|
||||
public void setDefaultConcurrencyPolicy(ConcurrencyPolicy defaultConcurrencyPolicy) {
|
||||
this.defaultConcurrencyPolicy = defaultConcurrencyPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to automatically start the bus after initialization.
|
||||
* <p>Default is 'true'; set this to 'false' to allow for manual startup
|
||||
* through the {@link #start()} method.
|
||||
*/
|
||||
public void setAutoStartup(boolean autoStartup) {
|
||||
this.autoStartup = autoStartup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether the bus should automatically create a channel when a
|
||||
* subscription contains the name of a previously unregistered channel.
|
||||
*/
|
||||
public void setAutoCreateChannels(boolean autoCreateChannels) {
|
||||
this.autoCreateChannels = autoCreateChannels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether the bus should configure its asynchronous task executor
|
||||
* to also be used by the ApplicationContext's 'applicationEventMulticaster'.
|
||||
* This will only apply if the multicaster defined within the context
|
||||
* is an instance of SimpleApplicationEventMulticaster (the default).
|
||||
* This property is 'false' by default.
|
||||
*/
|
||||
public void setConfigureAsyncEventMulticaster(boolean configureAsyncEventMulticaster) {
|
||||
this.configureAsyncEventMulticaster = configureAsyncEventMulticaster;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void registerChannels(ApplicationContext context) {
|
||||
Map<String, MessageChannel> channelBeans =
|
||||
(Map<String, MessageChannel>) context.getBeansOfType(MessageChannel.class);
|
||||
for (Map.Entry<String, MessageChannel> entry : channelBeans.entrySet()) {
|
||||
this.registerChannel(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void registerEndpoints(ApplicationContext context) {
|
||||
Map<String, MessageEndpoint> endpointBeans =
|
||||
(Map<String, MessageEndpoint>) context.getBeansOfType(MessageEndpoint.class);
|
||||
for (Map.Entry<String, MessageEndpoint> entry : endpointBeans.entrySet()) {
|
||||
this.registerEndpoint(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
synchronized (this.lifecycleMonitor) {
|
||||
if (this.initialized || this.initializing) {
|
||||
return;
|
||||
}
|
||||
this.initializing = true;
|
||||
if (this.executor == null) {
|
||||
this.executor = new ScheduledThreadPoolExecutor(DEFAULT_DISPATCHER_POOL_SIZE);
|
||||
}
|
||||
this.taskScheduler = new SimpleMessagingTaskScheduler(this.executor);
|
||||
if (this.getErrorChannel() == null) {
|
||||
this.setErrorChannel(new DefaultErrorChannel());
|
||||
}
|
||||
this.taskScheduler.setErrorHandler(new MessagePublishingErrorHandler(this.getErrorChannel()));
|
||||
this.initialized = true;
|
||||
this.initializing = false;
|
||||
}
|
||||
}
|
||||
|
||||
public MessageChannel getErrorChannel() {
|
||||
return this.lookupChannel(ERROR_CHANNEL_NAME);
|
||||
}
|
||||
|
||||
public void setErrorChannel(MessageChannel errorChannel) {
|
||||
this.registerChannel(ERROR_CHANNEL_NAME, errorChannel);
|
||||
}
|
||||
|
||||
public MessageChannel lookupChannel(String channelName) {
|
||||
return this.channelRegistry.lookupChannel(channelName);
|
||||
}
|
||||
|
||||
public void registerChannel(String name, MessageChannel channel) {
|
||||
if (!this.initialized) {
|
||||
this.initialize();
|
||||
}
|
||||
channel.setName(name);
|
||||
SubscriptionManager manager = new SubscriptionManager(channel, this.taskScheduler);
|
||||
this.subscriptionManagers.put(channel, manager);
|
||||
this.channelRegistry.registerChannel(name, channel);
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("registered channel '" + name + "'");
|
||||
}
|
||||
}
|
||||
|
||||
public MessageChannel unregisterChannel(String name) {
|
||||
MessageChannel removedChannel = this.channelRegistry.unregisterChannel(name);
|
||||
if (removedChannel != null) {
|
||||
SubscriptionManager manager = this.subscriptionManagers.remove(removedChannel);
|
||||
if (manager != null && manager.isRunning()) {
|
||||
manager.stop();
|
||||
}
|
||||
}
|
||||
return removedChannel;
|
||||
}
|
||||
|
||||
public void registerHandler(String name, MessageHandler handler, Subscription subscription) {
|
||||
this.registerHandler(name, handler, subscription, this.defaultConcurrencyPolicy);
|
||||
}
|
||||
|
||||
public void registerHandler(String name, MessageHandler handler, Subscription subscription, ConcurrencyPolicy concurrencyPolicy) {
|
||||
Assert.notNull(handler, "'handler' must not be null");
|
||||
HandlerEndpoint endpoint = new HandlerEndpoint(handler);
|
||||
this.doRegisterEndpoint(name, endpoint, subscription, concurrencyPolicy);
|
||||
}
|
||||
|
||||
public void registerTarget(String name, Target target, Subscription subscription) {
|
||||
this.registerTarget(name, target, subscription, this.defaultConcurrencyPolicy);
|
||||
}
|
||||
|
||||
public void registerTarget(String name, Target target, Subscription subscription, ConcurrencyPolicy concurrencyPolicy) {
|
||||
Assert.notNull(target, "'target' must not be null");
|
||||
TargetEndpoint endpoint = new TargetEndpoint(target);
|
||||
this.doRegisterEndpoint(name, endpoint, subscription, concurrencyPolicy);
|
||||
}
|
||||
|
||||
private void doRegisterEndpoint(String name, TargetEndpoint endpoint, Subscription subscription, ConcurrencyPolicy concurrencyPolicy) {
|
||||
endpoint.setName(name);
|
||||
endpoint.setSubscription(subscription);
|
||||
endpoint.setConcurrencyPolicy(concurrencyPolicy);
|
||||
this.registerEndpoint(name, endpoint);
|
||||
}
|
||||
|
||||
public void registerEndpoint(String name, MessageEndpoint endpoint) {
|
||||
if (!this.initialized) {
|
||||
this.initialize();
|
||||
}
|
||||
if (endpoint instanceof ChannelRegistryAware) {
|
||||
((ChannelRegistryAware) endpoint).setChannelRegistry(this.channelRegistry);
|
||||
}
|
||||
if (endpoint instanceof TargetEndpoint) {
|
||||
this.registerTargetEndpoint(name, (TargetEndpoint) endpoint);
|
||||
}
|
||||
else if (endpoint instanceof SourceEndpoint) {
|
||||
this.registerSourceEndpoint(name, (SourceEndpoint) endpoint);
|
||||
}
|
||||
this.endpointRegistry.registerEndpoint(name, endpoint);
|
||||
if (this.isRunning()) {
|
||||
activateEndpoint(endpoint);
|
||||
}
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("registered endpoint '" + name + "'");
|
||||
}
|
||||
}
|
||||
|
||||
private void registerTargetEndpoint(String name, TargetEndpoint endpoint) {
|
||||
if (endpoint.getConcurrencyPolicy() == null && this.defaultConcurrencyPolicy != null) {
|
||||
endpoint.setConcurrencyPolicy(this.defaultConcurrencyPolicy);
|
||||
}
|
||||
endpoint.afterPropertiesSet();
|
||||
}
|
||||
|
||||
public MessageEndpoint unregisterEndpoint(String name) {
|
||||
MessageEndpoint endpoint = this.endpointRegistry.unregisterEndpoint(name);
|
||||
if (endpoint == null) {
|
||||
return null;
|
||||
}
|
||||
if (endpoint instanceof TargetEndpoint) {
|
||||
Collection<SubscriptionManager> managers = this.subscriptionManagers.values();
|
||||
boolean removed = false;
|
||||
for (SubscriptionManager manager : managers) {
|
||||
removed = (removed || manager.removeTarget((TargetEndpoint) endpoint));
|
||||
}
|
||||
if (removed) {
|
||||
return endpoint;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public MessageEndpoint lookupEndpoint(String endpointName) {
|
||||
return this.endpointRegistry.lookupEndpoint(endpointName);
|
||||
}
|
||||
|
||||
public Set<String> getEndpointNames() {
|
||||
return this.endpointRegistry.getEndpointNames();
|
||||
}
|
||||
|
||||
private void activateEndpoints() {
|
||||
Set<String> endpointNames = this.endpointRegistry.getEndpointNames();
|
||||
for (String name : endpointNames) {
|
||||
MessageEndpoint endpoint = this.endpointRegistry.lookupEndpoint(name);
|
||||
if (endpoint != null) {
|
||||
this.activateEndpoint(endpoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void activateEndpoint(MessageEndpoint endpoint) {
|
||||
if (endpoint instanceof TargetEndpoint) {
|
||||
this.activateTargetEndpoint((TargetEndpoint) endpoint);
|
||||
}
|
||||
}
|
||||
|
||||
private void activateTargetEndpoint(TargetEndpoint endpoint) {
|
||||
Subscription subscription = endpoint.getSubscription();
|
||||
if (subscription == null) {
|
||||
throw new ConfigurationException("Unable to register endpoint '" +
|
||||
endpoint + "'. No subscription information is available.");
|
||||
}
|
||||
MessageChannel channel = subscription.getChannel();
|
||||
if (channel == null) {
|
||||
String channelName = subscription.getChannelName();
|
||||
if (channelName == null) {
|
||||
throw new ConfigurationException("endpoint '" + endpoint +
|
||||
"' must provide either 'channel' or 'channelName' in its subscription metadata");
|
||||
}
|
||||
channel = this.lookupChannel(channelName);
|
||||
if (channel == null) {
|
||||
if (this.autoCreateChannels == false) {
|
||||
throw new ConfigurationException("Cannot activate subscription, unknown channel '" + channelName +
|
||||
"'. Consider enabling the 'autoCreateChannels' option for the message bus.");
|
||||
}
|
||||
if (this.logger.isInfoEnabled()) {
|
||||
logger.info("auto-creating channel '" + channelName + "'");
|
||||
}
|
||||
channel = channelFactory.getChannel(null, null);
|
||||
this.registerChannel(channelName, channel);
|
||||
}
|
||||
}
|
||||
if (endpoint instanceof HandlerEndpoint) {
|
||||
HandlerEndpoint handlerEndpoint = (HandlerEndpoint) endpoint;
|
||||
String outputChannelName = handlerEndpoint.getOutputChannelName();
|
||||
if (outputChannelName != null && this.lookupChannel(outputChannelName) == null) {
|
||||
if (!this.autoCreateChannels) {
|
||||
throw new ConfigurationException("Unknown channel '" + outputChannelName +
|
||||
"' configured as output channel for endpoint '" + endpoint +
|
||||
"'. Consider enabling the 'autoCreateChannels' option for the message bus.");
|
||||
}
|
||||
this.registerChannel(outputChannelName, new QueueChannel());
|
||||
}
|
||||
}
|
||||
if (endpoint instanceof TargetEndpoint) {
|
||||
TargetEndpoint targetEndpoint = (TargetEndpoint) endpoint;
|
||||
if (!targetEndpoint.hasErrorHandler() && this.getErrorChannel() != null && !this.getErrorChannel().equals(channel)) {
|
||||
targetEndpoint.setErrorHandler(new MessagePublishingErrorHandler(this.getErrorChannel()));
|
||||
}
|
||||
}
|
||||
this.activateSubscription(channel, endpoint, subscription.getSchedule());
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("activated subscription to channel '" + channel.getName() +
|
||||
"' for endpoint '" + endpoint + "'");
|
||||
}
|
||||
}
|
||||
|
||||
private void registerSourceEndpoint(String name, SourceEndpoint endpoint) {
|
||||
if (!this.initialized) {
|
||||
this.initialize();
|
||||
}
|
||||
if (endpoint instanceof MessagingTask) {
|
||||
this.taskScheduler.schedule((MessagingTask) endpoint);
|
||||
}
|
||||
if (endpoint instanceof Lifecycle) {
|
||||
this.lifecycleEndpoints.add((Lifecycle) endpoint);
|
||||
if (this.isRunning()) {
|
||||
((Lifecycle) endpoint).start();
|
||||
}
|
||||
}
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("registered source adapter '" + name + "'");
|
||||
}
|
||||
}
|
||||
|
||||
private void activateSubscription(MessageChannel channel, Target target, Schedule schedule) {
|
||||
SubscriptionManager manager = this.subscriptionManagers.get(channel);
|
||||
if (manager == null) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("no subscription manager available for channel '" + channel + "', be sure to register the channel");
|
||||
}
|
||||
return;
|
||||
}
|
||||
manager.addTarget(target, schedule);
|
||||
if (this.isRunning() && !manager.isRunning()) {
|
||||
manager.start();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
synchronized (this.lifecycleMonitor) {
|
||||
return this.running;
|
||||
}
|
||||
}
|
||||
|
||||
public void start() {
|
||||
if (!this.initialized) {
|
||||
this.initialize();
|
||||
}
|
||||
if (this.isRunning() || this.starting) {
|
||||
return;
|
||||
}
|
||||
this.starting = true;
|
||||
synchronized (this.lifecycleMonitor) {
|
||||
this.activateEndpoints();
|
||||
this.taskScheduler.start();
|
||||
for (SubscriptionManager manager : this.subscriptionManagers.values()) {
|
||||
manager.start();
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("started subscription manager '" + manager + "'");
|
||||
}
|
||||
}
|
||||
for (Lifecycle endpoint : this.lifecycleEndpoints) {
|
||||
endpoint.start();
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("started endpoint '" + endpoint + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
this.running = true;
|
||||
this.starting = false;
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("message bus started");
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (!this.isRunning()) {
|
||||
return;
|
||||
}
|
||||
synchronized (this.lifecycleMonitor) {
|
||||
this.running = false;
|
||||
this.taskScheduler.stop();
|
||||
for (Lifecycle endpoint : this.lifecycleEndpoints) {
|
||||
endpoint.stop();
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("stopped endpoint '" + endpoint + "'");
|
||||
}
|
||||
}
|
||||
for (SubscriptionManager manager : this.subscriptionManagers.values()) {
|
||||
manager.stop();
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("stopped subscription manager '" + manager + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("message bus stopped");
|
||||
}
|
||||
}
|
||||
|
||||
public void onApplicationEvent(ApplicationEvent event) {
|
||||
if (event instanceof ContextRefreshedEvent) {
|
||||
ApplicationContext context = ((ContextRefreshedEvent) event).getApplicationContext();
|
||||
this.registerEndpoints(context);
|
||||
if (this.configureAsyncEventMulticaster) {
|
||||
this.initialize();
|
||||
this.doConfigureAsyncEventMulticaster(context);
|
||||
}
|
||||
if (this.autoStartup) {
|
||||
this.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void doConfigureAsyncEventMulticaster(ApplicationContext context) {
|
||||
String multicasterBeanName = AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME;
|
||||
if (context.containsBean(multicasterBeanName)) {
|
||||
ApplicationEventMulticaster multicaster =
|
||||
(ApplicationEventMulticaster) context.getBean(multicasterBeanName);
|
||||
if (multicaster instanceof SimpleApplicationEventMulticaster) {
|
||||
((SimpleApplicationEventMulticaster) multicaster).setTaskExecutor(this.taskScheduler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.bus;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by classes which need access to the {@link MessageBus}.
|
||||
* @author Marius Bogoevici
|
||||
*
|
||||
*/
|
||||
public interface MessageBusAware {
|
||||
|
||||
public void setMessageBus(MessageBus messageBus);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.bus;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A bean post processor which injects all {@link MessageBusAware} beans with a
|
||||
* reference to the {@link MessageBus}.
|
||||
*
|
||||
* @author Marius Bogoevici
|
||||
*
|
||||
*/
|
||||
public class MessageBusAwareBeanPostProcessor implements BeanPostProcessor {
|
||||
|
||||
private final MessageBus messageBus;
|
||||
|
||||
public MessageBusAwareBeanPostProcessor(MessageBus messageBus) {
|
||||
Assert.notNull(messageBus, "'messageBus' must not be null");
|
||||
this.messageBus = messageBus;
|
||||
}
|
||||
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (bean instanceof MessageBusAware) {
|
||||
((MessageBusAware) bean).setMessageBus(messageBus);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.bus;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.integration.ConfigurationException;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.channel.MessageChannel;
|
||||
import org.springframework.integration.dispatcher.DefaultPollingDispatcher;
|
||||
import org.springframework.integration.dispatcher.PollingDispatcherTask;
|
||||
import org.springframework.integration.endpoint.TargetEndpoint;
|
||||
import org.springframework.integration.message.MessagingException;
|
||||
import org.springframework.integration.message.Target;
|
||||
import org.springframework.integration.scheduling.MessagingTaskScheduler;
|
||||
import org.springframework.integration.scheduling.PollingSchedule;
|
||||
import org.springframework.integration.scheduling.Schedule;
|
||||
import org.springframework.integration.util.ErrorHandler;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Manages subscriptions for {@link Target Targets} to a {@link MessageChannel}
|
||||
* including the creation, scheduling, and lifecycle management of dispatchers.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class SubscriptionManager {
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
private final MessageChannel channel;
|
||||
|
||||
private final MessagingTaskScheduler scheduler;
|
||||
|
||||
private volatile Schedule defaultSchedule = new PollingSchedule(5);
|
||||
|
||||
private final ConcurrentMap<Schedule, PollingDispatcherTask> dispatcherTasks = new ConcurrentHashMap<Schedule, PollingDispatcherTask>();
|
||||
|
||||
private final List<Lifecycle> lifecycleTargets = new CopyOnWriteArrayList<Lifecycle>();
|
||||
|
||||
private volatile boolean running;
|
||||
|
||||
private final Object lifecycleMonitor = new Object();
|
||||
|
||||
|
||||
public SubscriptionManager(MessageChannel channel, MessagingTaskScheduler scheduler) {
|
||||
Assert.notNull(channel, "channel must not be null");
|
||||
Assert.notNull(scheduler, "scheduler must not be null");
|
||||
this.channel = channel;
|
||||
this.scheduler = scheduler;
|
||||
}
|
||||
|
||||
|
||||
public void setDefaultSchedule(Schedule defaultSchedule) {
|
||||
Assert.notNull(defaultSchedule, "'defaultSchedule' must not be null");
|
||||
this.defaultSchedule = defaultSchedule;
|
||||
}
|
||||
|
||||
public void addTarget(Target target) {
|
||||
this.addTarget(target, null);
|
||||
}
|
||||
|
||||
public void addTarget(Target target, Schedule schedule) {
|
||||
Assert.notNull(target, "'target' must not be null");
|
||||
if (schedule == null) {
|
||||
schedule = this.defaultSchedule;
|
||||
}
|
||||
else if (this.channel instanceof DirectChannel) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Subscribing to a SynchronousChannel. The provided schedule will be ignored.");
|
||||
}
|
||||
}
|
||||
else if (this.channel.getDispatcherPolicy().isPublishSubscribe()) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("This dispatcher broadcasts messages for a publish-subscribe channel. " +
|
||||
"Therefore all targets are scheduled with its 'defaultSchedule', " +
|
||||
"and the provided schedule will be ignored.");
|
||||
}
|
||||
schedule = this.defaultSchedule;
|
||||
}
|
||||
if (target instanceof Lifecycle) {
|
||||
this.lifecycleTargets.add((Lifecycle) target);
|
||||
if (this.isRunning()) {
|
||||
((Lifecycle) target).start();
|
||||
}
|
||||
}
|
||||
if (this.channel instanceof DirectChannel) {
|
||||
((DirectChannel) this.channel).subscribe(target);
|
||||
if (target instanceof TargetEndpoint) {
|
||||
((TargetEndpoint) target).setErrorHandler(new ErrorHandler() {
|
||||
public void handle(Throwable t) {
|
||||
if (t instanceof MessagingException) {
|
||||
throw (MessagingException) t;
|
||||
}
|
||||
throw new MessagingException("error occurred in handler", t);
|
||||
}
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
PollingDispatcherTask dispatcherTask = this.dispatcherTasks.get(schedule);
|
||||
if (dispatcherTask == null) {
|
||||
DefaultPollingDispatcher dispatcher = new DefaultPollingDispatcher(this.channel);
|
||||
dispatcherTask = this.dispatcherTasks.putIfAbsent(schedule, new PollingDispatcherTask(dispatcher, schedule));
|
||||
}
|
||||
this.dispatcherTasks.get(schedule).getDispatcher().subscribe(target);
|
||||
if (dispatcherTask == null && this.isRunning()) {
|
||||
this.scheduleDispatcherTask(schedule);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean removeTarget(Target target) {
|
||||
boolean removed = false;
|
||||
Collection<PollingDispatcherTask> dispatcherTaskValues = this.dispatcherTasks.values();
|
||||
for (PollingDispatcherTask dispatcherTask : dispatcherTaskValues) {
|
||||
removed = (removed || dispatcherTask.getDispatcher().unsubscribe(target));
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return this.running;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
synchronized (this.lifecycleMonitor) {
|
||||
if (this.running) {
|
||||
return;
|
||||
}
|
||||
if (this.scheduler == null) {
|
||||
throw new ConfigurationException("scheduler is required");
|
||||
}
|
||||
if (!this.scheduler.isRunning()) {
|
||||
this.scheduler.start();
|
||||
}
|
||||
for (Lifecycle target : lifecycleTargets) {
|
||||
target.start();
|
||||
}
|
||||
for (Schedule schedule : this.dispatcherTasks.keySet()) {
|
||||
this.scheduleDispatcherTask(schedule);
|
||||
}
|
||||
this.running = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleDispatcherTask(Schedule schedule) {
|
||||
PollingDispatcherTask dispatcherTask = this.dispatcherTasks.get(schedule);
|
||||
if (dispatcherTask != null) {
|
||||
this.scheduler.schedule(dispatcherTask);
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
synchronized (this.lifecycleMonitor) {
|
||||
if (!this.running) {
|
||||
return;
|
||||
}
|
||||
for (Lifecycle target : lifecycleTargets) {
|
||||
target.stop();
|
||||
}
|
||||
this.running = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.channel;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.integration.message.Message;
|
||||
|
||||
/**
|
||||
* Base class for {@link MessageChannel} implementations providing common
|
||||
* properties such as the channel name and {@link DispatcherPolicy}. Also
|
||||
* provides the common functionality for sending and receiving
|
||||
* {@link Message Messages} including the invocation of any
|
||||
* {@link ChannelInterceptor ChannelInterceptors}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public abstract class AbstractMessageChannel implements MessageChannel, BeanNameAware {
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
private volatile String name;
|
||||
|
||||
private final ChannelInterceptorList interceptors = new ChannelInterceptorList();
|
||||
|
||||
private final DispatcherPolicy dispatcherPolicy;
|
||||
|
||||
|
||||
/**
|
||||
* Create a channel with the given dispatcher policy.
|
||||
*/
|
||||
public AbstractMessageChannel(DispatcherPolicy dispatcherPolicy) {
|
||||
this.dispatcherPolicy = dispatcherPolicy;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the name of this channel.
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of this channel.
|
||||
*/
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of this channel to its bean name. This will be invoked
|
||||
* automatically whenever the channel is configured explicitly with a bean
|
||||
* definition.
|
||||
*/
|
||||
public void setBeanName(String beanName) {
|
||||
this.setName(beanName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the list of channel interceptors. This will clear any existing
|
||||
* interceptors.
|
||||
*/
|
||||
public void setInterceptors(List<ChannelInterceptor> interceptors) {
|
||||
this.interceptors.set(interceptors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a channel interceptor to the end of the list.
|
||||
*/
|
||||
public void addInterceptor(ChannelInterceptor interceptor) {
|
||||
this.interceptors.add(interceptor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the dispatcher policy for this channel.
|
||||
*/
|
||||
public DispatcherPolicy getDispatcherPolicy() {
|
||||
return this.dispatcherPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message on this channel. If the channel is at capacity, this
|
||||
* method will block until either space becomes available or the sending
|
||||
* thread is interrupted.
|
||||
*
|
||||
* @param message the Message to send
|
||||
*
|
||||
* @return <code>true</code> if the message is sent successfully or
|
||||
* <code>false</code> if the sending thread is interrupted.
|
||||
*/
|
||||
public final boolean send(Message<?> message) {
|
||||
return this.send(message, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message on this channel. If the channel is at capacity, this
|
||||
* method will block until either the timeout occurs or the sending thread
|
||||
* is interrupted. If the specified timeout is 0, the method will return
|
||||
* immediately. If less than zero, it will block indefinitely (see
|
||||
* {@link #send(Message)}).
|
||||
*
|
||||
* @param message the Message to send
|
||||
* @param timeout the timeout in milliseconds
|
||||
*
|
||||
* @return <code>true</code> if the message is sent successfully,
|
||||
* <code>false</code> if the message cannot be sent within the allotted
|
||||
* time or the sending thread is interrupted.
|
||||
*/
|
||||
public final boolean send(Message<?> message, long timeout) {
|
||||
if (!this.interceptors.preSend(message, this)) {
|
||||
return false;
|
||||
}
|
||||
boolean sent = this.doSend(message, timeout);
|
||||
this.interceptors.postSend(message, this, sent);
|
||||
return sent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive the first available message from this channel. If the channel
|
||||
* contains no messages, this method will block.
|
||||
*
|
||||
* @return the first available message or <code>null</code> if the
|
||||
* receiving thread is interrupted.
|
||||
*/
|
||||
public final Message<?> receive() {
|
||||
return this.receive(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive the first available message from this channel. If the channel
|
||||
* contains no messages, this method will block until the allotted timeout
|
||||
* elapses. If the specified timeout is 0, the method will return
|
||||
* immediately. If less than zero, it will block indefinitely (see
|
||||
* {@link #receive()}).
|
||||
*
|
||||
* @param timeout the timeout in milliseconds
|
||||
*
|
||||
* @return the first available message or <code>null</code> if no message
|
||||
* is available within the allotted time or the receiving thread is
|
||||
* interrupted.
|
||||
*/
|
||||
public final Message<?> receive(long timeout) {
|
||||
if (!this.interceptors.preReceive(this)) {
|
||||
return null;
|
||||
}
|
||||
Message<?> message = this.doReceive(timeout);
|
||||
this.interceptors.postReceive(message, this);
|
||||
return message;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return (this.name != null) ? this.name : super.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Subclasses must implement this method. A non-negative timeout indicates
|
||||
* how long to wait if the channel is at capacity (if the value is 0, it
|
||||
* must return immediately with or without success). A negative timeout
|
||||
* value indicates that the method should block until either the message is
|
||||
* accepted or the blocking thread is interrupted.
|
||||
*/
|
||||
protected abstract boolean doSend(Message<?> message, long timeout);
|
||||
|
||||
/**
|
||||
* Subclasses must implement this method. A non-negative timeout indicates
|
||||
* how long to wait if the channel is empty (if the value is 0, it must
|
||||
* return immediately with or without success). A negative timeout value
|
||||
* indicates that the method should block until either a message is
|
||||
* available or the blocking thread is interrupted.
|
||||
*/
|
||||
protected abstract Message<?> doReceive(long timeout);
|
||||
|
||||
|
||||
/**
|
||||
* A convenience wrapper class for the list of ChannelInterceptors.
|
||||
*/
|
||||
private class ChannelInterceptorList {
|
||||
|
||||
private final List<ChannelInterceptor> interceptors = new CopyOnWriteArrayList<ChannelInterceptor>();
|
||||
|
||||
|
||||
public boolean set(List<ChannelInterceptor> interceptors) {
|
||||
synchronized (this.interceptors) {
|
||||
this.interceptors.clear();
|
||||
return this.interceptors.addAll(interceptors);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean add(ChannelInterceptor interceptor) {
|
||||
return this.interceptors.add(interceptor);
|
||||
}
|
||||
|
||||
public boolean preSend(Message<?> message, MessageChannel channel) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("preSend on channel '" + channel + "', message: " + message);
|
||||
}
|
||||
for (ChannelInterceptor interceptor : interceptors) {
|
||||
if (!interceptor.preSend(message, channel)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void postSend(Message<?> message, MessageChannel channel, boolean sent) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("postSend (sent=" + sent + ") on channel '" + channel + "', message: " + message);
|
||||
}
|
||||
for (ChannelInterceptor interceptor : interceptors) {
|
||||
interceptor.postSend(message, channel, sent);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean preReceive(MessageChannel channel) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("preReceive on channel '" + channel + "'");
|
||||
}
|
||||
for (ChannelInterceptor interceptor : interceptors) {
|
||||
if (!interceptor.preReceive(channel)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void postReceive(Message<?> message, MessageChannel channel) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("postReceive on channel '" + channel + "', message: " + message);
|
||||
}
|
||||
for (ChannelInterceptor interceptor : interceptors) {
|
||||
interceptor.postReceive(message, channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.channel;
|
||||
|
||||
import org.springframework.integration.message.Message;
|
||||
|
||||
/**
|
||||
* Interface for interceptors that are able to view and/or modify the
|
||||
* {@link Message Messages} being sent-to and/or received-from a
|
||||
* {@link MessageChannel}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public interface ChannelInterceptor {
|
||||
|
||||
boolean preSend(Message<?> message, MessageChannel channel);
|
||||
|
||||
void postSend(Message<?> message, MessageChannel channel, boolean sent);
|
||||
|
||||
boolean preReceive(MessageChannel channel);
|
||||
|
||||
void postReceive(Message<?> message, MessageChannel channel);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.channel;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Sends to a channel and provides a configurable timeout. Convenient for either
|
||||
* subclassing or delegation from components that need to publish to a channel.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class ChannelPublisher {
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
private volatile MessageChannel channel;
|
||||
|
||||
private volatile long timeout = 0;
|
||||
|
||||
|
||||
public ChannelPublisher() {
|
||||
}
|
||||
|
||||
public ChannelPublisher(MessageChannel channel) {
|
||||
this.setChannel(channel);
|
||||
}
|
||||
|
||||
|
||||
public void setChannel(MessageChannel channel) {
|
||||
Assert.notNull(channel, "channel must not be null");
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
public void setTimeout(long timeout) {
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
protected MessageChannel getChannel() {
|
||||
return this.channel;
|
||||
}
|
||||
|
||||
public boolean publish(Message<?> message) {
|
||||
if (this.channel == null) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("unable to send message, no channel available");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (message == null) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("null messages are not supported");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return (this.timeout < 0) ? this.channel.send(message) : this.channel.send(message, this.timeout);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.channel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.message.selector.MessageSelector;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A utility class for purging {@link Message Messages} from one or more
|
||||
* {@link MessageChannel MessageChannels}. Any message that does <em>not</em>
|
||||
* match the provided {@link MessageSelector} will be removed from the channel.
|
||||
* If no {@link MessageSelector} is provided, then <em>all</em> messages will be
|
||||
* cleared from the channel.
|
||||
* <p>
|
||||
* Note that the {@link #purge()} method operates on a snapshot of the messages
|
||||
* within a channel at the time that the method is invoked. It is therefore
|
||||
* possible that new messages will arrive on the channel during the purge
|
||||
* operation and thus will <em>not</em> be removed. Likewise, messages to be
|
||||
* purged may have been removed from the channel while the operation is taking
|
||||
* place. Such messages will not be included in the returned list.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class ChannelPurger {
|
||||
|
||||
private final MessageChannel[] channels;
|
||||
|
||||
private final MessageSelector selector;
|
||||
|
||||
|
||||
public ChannelPurger(MessageChannel ... channels) {
|
||||
this(null, channels);
|
||||
}
|
||||
|
||||
public ChannelPurger(MessageSelector selector, MessageChannel ... channels) {
|
||||
Assert.notEmpty(channels, "at least one channel is required");
|
||||
if (channels.length == 1) {
|
||||
Assert.notNull(channels[0], "channel must not be null");
|
||||
}
|
||||
this.selector = selector;
|
||||
this.channels = channels;
|
||||
}
|
||||
|
||||
|
||||
public final List<Message<?>> purge() {
|
||||
List<Message<?>> purgedMessages = new ArrayList<Message<?>>();
|
||||
for (MessageChannel channel : this.channels) {
|
||||
List<Message<?>> results = (this.selector == null) ?
|
||||
channel.clear() : channel.purge(this.selector);
|
||||
if (results != null) {
|
||||
purgedMessages.addAll(results);
|
||||
}
|
||||
}
|
||||
return purgedMessages;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.channel;
|
||||
|
||||
/**
|
||||
* A strategy interface for registration and lookup of message channels by name.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public interface ChannelRegistry {
|
||||
|
||||
void registerChannel(String name, MessageChannel channel);
|
||||
|
||||
MessageChannel unregisterChannel(String name);
|
||||
|
||||
MessageChannel lookupChannel(String channelName);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.channel;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by components that need access to the
|
||||
* {@link ChannelRegistry}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public interface ChannelRegistryAware {
|
||||
|
||||
void setChannelRegistry(ChannelRegistry channelRegistry);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.channel;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A simple map-backed implementation of {@link ChannelRegistry}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class DefaultChannelRegistry implements ChannelRegistry {
|
||||
|
||||
private final Map<String, MessageChannel> channels = new ConcurrentHashMap<String, MessageChannel>();
|
||||
|
||||
|
||||
public MessageChannel lookupChannel(String channelName) {
|
||||
return this.channels.get(channelName);
|
||||
}
|
||||
|
||||
public void registerChannel(String name, MessageChannel channel) {
|
||||
Assert.notNull(name, "'name' must not be null");
|
||||
Assert.notNull(channel, "'channel' must not be null");
|
||||
this.channels.put(name, channel);
|
||||
}
|
||||
|
||||
public MessageChannel unregisterChannel(String name) {
|
||||
return (name != null) ? this.channels.remove(name) : null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.channel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.springframework.integration.dispatcher.SimpleDispatcher;
|
||||
import org.springframework.integration.handler.MessageHandler;
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.message.Source;
|
||||
import org.springframework.integration.message.Subscribable;
|
||||
import org.springframework.integration.message.Target;
|
||||
import org.springframework.integration.message.selector.MessageSelector;
|
||||
|
||||
/**
|
||||
* A channel that invokes the subscribed {@link MessageHandler handler(s)} in a
|
||||
* sender's thread (returning after at most one handles the message). If a
|
||||
* {@link Source} is provided, then that source will likewise be polled
|
||||
* within a receiver's thread.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class DirectChannel extends AbstractMessageChannel implements Subscribable {
|
||||
|
||||
private volatile Source<?> source;
|
||||
|
||||
private final SimpleDispatcher dispatcher;
|
||||
|
||||
private final AtomicInteger handlerCount = new AtomicInteger();
|
||||
|
||||
|
||||
public DirectChannel() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public DirectChannel(Source<?> source) {
|
||||
super(defaultDispatcherPolicy());
|
||||
this.source = source;
|
||||
this.dispatcher = new SimpleDispatcher(this.getDispatcherPolicy());
|
||||
}
|
||||
|
||||
|
||||
public boolean subscribe(Target target) {
|
||||
boolean added = this.dispatcher.subscribe(target);
|
||||
if (added) {
|
||||
this.handlerCount.incrementAndGet();
|
||||
}
|
||||
return added;
|
||||
}
|
||||
|
||||
public boolean unsubscribe(Target target) {
|
||||
boolean removed = this.dispatcher.unsubscribe(target);
|
||||
if (removed) {
|
||||
this.handlerCount.decrementAndGet();
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Message<?> doReceive(long timeout) {
|
||||
if (this.source != null) {
|
||||
return this.source.receive();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doSend(Message<?> message, long timeout) {
|
||||
if (message != null && this.handlerCount.get() > 0) {
|
||||
return this.dispatcher.dispatch(message);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<Message<?>> clear() {
|
||||
return new ArrayList<Message<?>>();
|
||||
}
|
||||
|
||||
public List<Message<?>> purge(MessageSelector selector) {
|
||||
return new ArrayList<Message<?>>();
|
||||
}
|
||||
|
||||
|
||||
private static DispatcherPolicy defaultDispatcherPolicy() {
|
||||
DispatcherPolicy dispatcherPolicy = new DispatcherPolicy(false);
|
||||
dispatcherPolicy.setMaxMessagesPerTask(1);
|
||||
dispatcherPolicy.setReceiveTimeout(0);
|
||||
dispatcherPolicy.setRejectionLimit(1);
|
||||
dispatcherPolicy.setRetryInterval(0);
|
||||
dispatcherPolicy.setShouldFailOnRejectionLimit(false);
|
||||
return dispatcherPolicy;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.channel;
|
||||
|
||||
import org.springframework.integration.dispatcher.MessageDispatcher;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Metadata for a {@link MessageDispatcher}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class DispatcherPolicy {
|
||||
|
||||
public final static int DEFAULT_MAX_MESSAGES_PER_TASK = 1;
|
||||
|
||||
public final static long DEFAULT_RECEIVE_TIMEOUT = 1000;
|
||||
|
||||
public final static int DEFAULT_REJECTION_LIMIT = 5;
|
||||
|
||||
public final static long DEFAULT_RETRY_INTERVAL = 1000;
|
||||
|
||||
|
||||
private final boolean publishSubscribe;
|
||||
|
||||
private volatile int maxMessagesPerTask = DEFAULT_MAX_MESSAGES_PER_TASK;
|
||||
|
||||
private volatile long receiveTimeout = DEFAULT_RECEIVE_TIMEOUT;
|
||||
|
||||
private volatile int rejectionLimit = DEFAULT_REJECTION_LIMIT;
|
||||
|
||||
private volatile long retryInterval = DEFAULT_RETRY_INTERVAL;
|
||||
|
||||
private volatile boolean shouldFailOnRejectionLimit = true;
|
||||
|
||||
|
||||
public DispatcherPolicy() {
|
||||
this.publishSubscribe = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a DispatcherPolicy.
|
||||
*
|
||||
* @param publishSubscribe whether the dispatcher should attempt to publish
|
||||
* to all of its subscribed handlers. If '<code>false</code>' it will attempt
|
||||
* to send to a single handler (point-to-point).
|
||||
*/
|
||||
public DispatcherPolicy(boolean publishSubscribe) {
|
||||
this.publishSubscribe = publishSubscribe;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return whether the dispatcher should attempt to publish to all of its handlers.
|
||||
* This property is immutable.
|
||||
*/
|
||||
public boolean isPublishSubscribe() {
|
||||
return this.publishSubscribe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum number of messages for each retrieval attempt.
|
||||
*/
|
||||
public int getMaxMessagesPerTask() {
|
||||
return this.maxMessagesPerTask;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum number of messages for each retrieval attempt.
|
||||
*/
|
||||
public void setMaxMessagesPerTask(int maxMessagesPerTask) {
|
||||
Assert.isTrue(maxMessagesPerTask > 0, "'maxMessagePerTask' must be at least 1");
|
||||
this.maxMessagesPerTask = maxMessagesPerTask;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum amount of time in milliseconds to wait for a message to be available.
|
||||
*/
|
||||
public long getReceiveTimeout() {
|
||||
return this.receiveTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum amount of time in milliseconds to wait for a message to be available.
|
||||
*/
|
||||
public void setReceiveTimeout(long receiveTimeout) {
|
||||
this.receiveTimeout = receiveTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum number of retries upon rejection.
|
||||
*/
|
||||
public int getRejectionLimit() {
|
||||
return this.rejectionLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum number of retries upon rejection.
|
||||
*/
|
||||
public void setRejectionLimit(int rejectionLimit) {
|
||||
Assert.isTrue(rejectionLimit > 0, "'rejectionLimit' must be at least 1");
|
||||
this.rejectionLimit = rejectionLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the amount of time in milliseconds to wait between rejections.
|
||||
*/
|
||||
public long getRetryInterval() {
|
||||
return this.retryInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the amount of time in milliseconds to wait between rejections.
|
||||
*/
|
||||
public void setRetryInterval(long retryInterval) {
|
||||
Assert.isTrue(retryInterval >= 0, "'retryInterval' must not be negative");
|
||||
this.retryInterval = retryInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether an exception should be thrown when this dispatcher's
|
||||
* {@link #rejectionLimit} is reached.
|
||||
*/
|
||||
public boolean getShouldFailOnRejectionLimit() {
|
||||
return this.shouldFailOnRejectionLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether an exception should be thrown when this dispatcher's
|
||||
* {@link #rejectionLimit} is reached. The default value is 'true'.
|
||||
*/
|
||||
public void setShouldFailOnRejectionLimit(boolean shouldFailOnRejectionLimit) {
|
||||
this.shouldFailOnRejectionLimit = shouldFailOnRejectionLimit;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.channel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.integration.message.BlockingSource;
|
||||
import org.springframework.integration.message.BlockingTarget;
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.message.selector.MessageSelector;
|
||||
|
||||
/**
|
||||
* Base channel interface defining common behavior for message sending and receiving.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public interface MessageChannel extends BlockingSource, BlockingTarget {
|
||||
|
||||
/**
|
||||
* Return the name of this channel.
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Set the name of this channel.
|
||||
*/
|
||||
void setName(String name);
|
||||
|
||||
/**
|
||||
* Return this channel's dispatcher policy
|
||||
*/
|
||||
DispatcherPolicy getDispatcherPolicy();
|
||||
|
||||
/**
|
||||
* Remove all {@link Message Messages} from this channel.
|
||||
*/
|
||||
List<Message<?>> clear();
|
||||
|
||||
/**
|
||||
* Remove any {@link Message Messages} that are not accepted by the provided selector.
|
||||
*/
|
||||
List<Message<?>> purge(MessageSelector selector);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.channel;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.concurrent.PriorityBlockingQueue;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.message.MessageHeader;
|
||||
import org.springframework.integration.message.MessagePriority;
|
||||
|
||||
/**
|
||||
* A message channel that prioritizes messages based on a {@link Comparator}.
|
||||
* The default comparator is based upon the message header's 'priority'.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class PriorityChannel extends QueueChannel {
|
||||
|
||||
private final Semaphore semaphore;
|
||||
|
||||
|
||||
/**
|
||||
* Create a channel with the specified queue capacity and dispatcher policy.
|
||||
* Priority will be based upon the provided {@link Comparator}.
|
||||
*/
|
||||
public PriorityChannel(int capacity, DispatcherPolicy dispatcherPolicy, Comparator<Message<?>> comparator) {
|
||||
super(new PriorityBlockingQueue<Message<?>>(capacity, comparator), dispatcherPolicy);
|
||||
this.semaphore = new Semaphore(capacity, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a channel with the specified queue capacity and dispatcher policy.
|
||||
* Priority will be based upon the value of {@link MessageHeader#getPriority()}.
|
||||
*/
|
||||
public PriorityChannel(int capacity, DispatcherPolicy dispatcherPolicy) {
|
||||
this(capacity, dispatcherPolicy, new MessagePriorityComparator());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a channel with the specified queue capacity and default dispatcher
|
||||
* policy. Priority will be based on the value of {@link MessageHeader#getPriority()}.
|
||||
*/
|
||||
public PriorityChannel(int capacity) {
|
||||
this(capacity, null, new MessagePriorityComparator());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a channel with the default queue capacity and dispatcher policy.
|
||||
* Priority will be based on the value of {@link MessageHeader#getPriority()}.
|
||||
*/
|
||||
public PriorityChannel() {
|
||||
this(DEFAULT_CAPACITY, null, new MessagePriorityComparator());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean doSend(Message<?> message, long timeout) {
|
||||
try {
|
||||
if (!this.semaphore.tryAcquire(timeout, TimeUnit.MILLISECONDS)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
return false;
|
||||
}
|
||||
return super.doSend(message, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Message<?> doReceive(long timeout) {
|
||||
Message<?> message = super.doReceive(timeout);
|
||||
if (message != null) {
|
||||
this.semaphore.release();
|
||||
return message;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private static class MessagePriorityComparator implements Comparator<Message<?>> {
|
||||
|
||||
public int compare(Message<?> message1, Message<?> message2) {
|
||||
MessagePriority priority1 = message1.getHeader().getPriority();
|
||||
MessagePriority priority2 = message2.getHeader().getPriority();
|
||||
return priority1.compareTo(priority2);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.channel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.message.selector.MessageSelector;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Simple implementation of a message channel. Each {@link Message} is placed in
|
||||
* a {@link BlockingQueue} whose capacity may be specified upon construction.
|
||||
* The capacity must be a positive integer value. For a zero-capacity version
|
||||
* based upon a {@link java.util.concurrent.SynchronousQueue}, consider the
|
||||
* {@link RendezvousChannel}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class QueueChannel extends AbstractMessageChannel {
|
||||
|
||||
public static final int DEFAULT_CAPACITY = 100;
|
||||
|
||||
|
||||
private final BlockingQueue<Message<?>> queue;
|
||||
|
||||
|
||||
/**
|
||||
* Create a channel with the specified queue and dispatcher policy.
|
||||
*/
|
||||
public QueueChannel(BlockingQueue<Message<?>> queue, DispatcherPolicy dispatcherPolicy) {
|
||||
super((dispatcherPolicy != null) ? dispatcherPolicy : new DispatcherPolicy());
|
||||
Assert.notNull(queue, "'queue' must not be null");
|
||||
this.queue = queue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a channel with the specified queue capacity and dispatcher policy.
|
||||
*/
|
||||
public QueueChannel(int capacity, DispatcherPolicy dispatcherPolicy) {
|
||||
super((dispatcherPolicy != null) ? dispatcherPolicy : new DispatcherPolicy());
|
||||
Assert.isTrue(capacity > 0, "The capacity must be a positive integer. " +
|
||||
"For a zero-capacity alternative, consider '" + RendezvousChannel.class + "'");
|
||||
this.queue = new LinkedBlockingQueue<Message<?>>(capacity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a channel with the specified queue capacity.
|
||||
*/
|
||||
public QueueChannel(int capacity) {
|
||||
this(capacity, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a channel with the default queue capacity.
|
||||
* @see #DEFAULT_CAPACITY
|
||||
*/
|
||||
public QueueChannel() {
|
||||
this(DEFAULT_CAPACITY, null);
|
||||
}
|
||||
|
||||
|
||||
protected boolean doSend(Message<?> message, long timeout) {
|
||||
Assert.notNull(message, "'message' must not be null");
|
||||
try {
|
||||
if (timeout > 0) {
|
||||
return this.queue.offer(message, timeout, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
if (timeout == 0) {
|
||||
return this.queue.offer(message);
|
||||
}
|
||||
queue.put(message);
|
||||
return true;
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected Message<?> doReceive(long timeout) {
|
||||
try {
|
||||
if (timeout > 0) {
|
||||
return queue.poll(timeout, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
if (timeout == 0) {
|
||||
return queue.poll();
|
||||
}
|
||||
return queue.take();
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public List<Message<?>> clear() {
|
||||
List<Message<?>> clearedMessages = new ArrayList<Message<?>>();
|
||||
this.queue.drainTo(clearedMessages);
|
||||
return clearedMessages;
|
||||
}
|
||||
|
||||
public List<Message<?>> purge(MessageSelector selector) {
|
||||
if (selector == null) {
|
||||
return this.clear();
|
||||
}
|
||||
List<Message<?>> purgedMessages = new ArrayList<Message<?>>();
|
||||
Object[] array = this.queue.toArray();
|
||||
for (Object o : array) {
|
||||
Message<?> message = (Message<?>) o;
|
||||
if (!selector.accept(message) && this.queue.remove(message)) {
|
||||
purgedMessages.add(message);
|
||||
}
|
||||
}
|
||||
return purgedMessages;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.channel;
|
||||
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
|
||||
import org.springframework.integration.message.Message;
|
||||
|
||||
/**
|
||||
* A zero-capacity version of {@link QueueChannel} that delegates to a
|
||||
* {@link SynchronousQueue} internally. This accommodates "handoff" scenarios
|
||||
* (i.e. blocking while waiting for another party to send or receive).
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class RendezvousChannel extends QueueChannel {
|
||||
|
||||
public RendezvousChannel(DispatcherPolicy dispatcherPolicy) {
|
||||
super(new SynchronousQueue<Message<?>>(), dispatcherPolicy);
|
||||
}
|
||||
|
||||
public RendezvousChannel() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.channel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.integration.ConfigurationException;
|
||||
import org.springframework.integration.bus.MessageBus;
|
||||
import org.springframework.integration.bus.MessageBusAware;
|
||||
import org.springframework.integration.endpoint.EndpointRegistry;
|
||||
import org.springframework.integration.endpoint.HandlerEndpoint;
|
||||
import org.springframework.integration.handler.ReplyHandler;
|
||||
import org.springframework.integration.handler.ReplyMessageCorrelator;
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.message.MessagingException;
|
||||
import org.springframework.integration.message.selector.MessageSelector;
|
||||
import org.springframework.integration.scheduling.Subscription;
|
||||
|
||||
/**
|
||||
* A template that facilitates the implementation of request-reply usage
|
||||
* scenarios above one-way {@link MessageChannel MessageChannels}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class RequestReplyTemplate implements MessageBusAware {
|
||||
|
||||
private MessageChannel requestChannel;
|
||||
|
||||
private MessageChannel replyChannel;
|
||||
|
||||
private volatile long requestTimeout = -1;
|
||||
|
||||
private volatile long replyTimeout = -1;
|
||||
|
||||
private ReplyMessageCorrelator replyMessageCorrelator;
|
||||
|
||||
private EndpointRegistry endpointRegistry;
|
||||
|
||||
private final Object replyMessageCorrelatorMonitor = new Object();
|
||||
|
||||
|
||||
/**
|
||||
* Create a RequestReplyTemplate.
|
||||
*
|
||||
* @param requestChannel the channel to which request messages will be sent
|
||||
* @param replyChannel the channel from which reply messages will be received
|
||||
*/
|
||||
public RequestReplyTemplate(MessageChannel requestChannel, MessageChannel replyChannel) {
|
||||
this.requestChannel = requestChannel;
|
||||
this.replyChannel = replyChannel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a RequestReplyTemplate that will use anonymous temporary channels for replies.
|
||||
*
|
||||
* @param requestChannel the channel to which request messages will be sent
|
||||
*/
|
||||
public RequestReplyTemplate(MessageChannel requestChannel) {
|
||||
this(requestChannel, null);
|
||||
}
|
||||
|
||||
public RequestReplyTemplate() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the request channel.
|
||||
*
|
||||
* @param requestChannel the channel to which request messages will be sent
|
||||
*/
|
||||
public void setRequestChannel(MessageChannel requestChannel) {
|
||||
this.requestChannel = requestChannel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the reply channel. If no reply channel is provided, this template will
|
||||
* always use an anonymous, temporary channel for handling replies.
|
||||
*
|
||||
* @param replyChannel the channel from which reply messages will be received
|
||||
*/
|
||||
public void setReplyChannel(MessageChannel replyChannel) {
|
||||
this.replyChannel = replyChannel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the timeout value for sending request messages. If not
|
||||
* explicitly configured, the default is an indefinite timeout.
|
||||
*
|
||||
* @param requestTimeout the timeout value in milliseconds
|
||||
*/
|
||||
public void setRequestTimeout(long requestTimeout) {
|
||||
this.requestTimeout = requestTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the timeout value for receiving reply messages. If not
|
||||
* explicitly configured, the default is an indefinite timeout.
|
||||
*
|
||||
* @param replyTimeout the timeout value in milliseconds
|
||||
*/
|
||||
public void setReplyTimeout(long replyTimeout) {
|
||||
this.replyTimeout = replyTimeout;
|
||||
}
|
||||
|
||||
public void setEndpointRegistry(EndpointRegistry endpointRegistry) {
|
||||
this.endpointRegistry = endpointRegistry;
|
||||
}
|
||||
|
||||
public void setMessageBus(MessageBus messageBus) {
|
||||
if (this.endpointRegistry == null) {
|
||||
this.setEndpointRegistry(messageBus);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean send(Message<?> message) {
|
||||
if (message == null) {
|
||||
throw new MessagingException("Message must not be null.");
|
||||
}
|
||||
if (this.requestChannel == null) {
|
||||
throw new MessagingException("No request channel has been configured. Cannot send message.");
|
||||
}
|
||||
boolean sent = (this.requestTimeout >= 0) ?
|
||||
this.requestChannel.send(message, this.requestTimeout) : this.requestChannel.send(message);
|
||||
if (!sent) {
|
||||
throw new MessagingException("Failed to send request message.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public Message<?> receive() {
|
||||
if (this.replyChannel == null) {
|
||||
throw new MessagingException("No reply channel has been configured. Cannot perform receive only operation.");
|
||||
}
|
||||
return this.receiveResponse(this.replyChannel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a request message whose reply should be handled be the provided callback.
|
||||
*/
|
||||
public boolean request(Message<?> message, ReplyHandler replyHandler) {
|
||||
MessageChannel replyChannelAdapter = new ReplyHandlingChannelAdapter(message, replyHandler);
|
||||
message.getHeader().setReturnAddress(replyChannelAdapter);
|
||||
return this.send(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a request message and wait for a reply message using the configured
|
||||
* timeout values.
|
||||
*
|
||||
* @param requestMessage the request message to send
|
||||
*
|
||||
* @return the reply message or <code>null</code>
|
||||
*/
|
||||
public Message<?> request(Message<?> message) {
|
||||
if (this.requestChannel == null) {
|
||||
throw new MessagingException("No request channel available. Cannot send request message.");
|
||||
}
|
||||
if (this.replyChannel != null) {
|
||||
return this.sendAndReceiveWithReplyMessageCorrelator(message);
|
||||
}
|
||||
else {
|
||||
return this.sendAndReceiveWithTemporaryChannel(message);
|
||||
}
|
||||
}
|
||||
|
||||
private Message<?> sendAndReceiveWithReplyMessageCorrelator(Message<?> message) {
|
||||
if (this.replyMessageCorrelator == null) {
|
||||
this.registerReplyMessageCorrelator();
|
||||
}
|
||||
message.getHeader().setReturnAddress(this.replyChannel);
|
||||
this.send(message);
|
||||
return (this.replyTimeout >= 0) ? this.replyMessageCorrelator.getReply(message.getId(), this.replyTimeout) :
|
||||
this.replyMessageCorrelator.getReply(message.getId());
|
||||
}
|
||||
|
||||
private Message<?> sendAndReceiveWithTemporaryChannel(Message<?> message) {
|
||||
RendezvousChannel temporaryChannel = new RendezvousChannel();
|
||||
message.getHeader().setReturnAddress(temporaryChannel);
|
||||
this.send(message);
|
||||
return this.receiveResponse(temporaryChannel);
|
||||
}
|
||||
|
||||
private Message<?> receiveResponse(MessageChannel channel) {
|
||||
return (this.replyTimeout >= 0) ? channel.receive(this.replyTimeout) : channel.receive();
|
||||
}
|
||||
|
||||
private void registerReplyMessageCorrelator() {
|
||||
synchronized (this.replyMessageCorrelatorMonitor) {
|
||||
if (this.replyMessageCorrelator != null) {
|
||||
return;
|
||||
}
|
||||
if (this.endpointRegistry == null) {
|
||||
throw new ConfigurationException("No EndpointRegistry available. Cannot register ResponseCorrelator.");
|
||||
}
|
||||
ReplyMessageCorrelator correlator = new ReplyMessageCorrelator(10);
|
||||
HandlerEndpoint endpoint = new HandlerEndpoint(correlator);
|
||||
endpoint.setSubscription(new Subscription(this.replyChannel));
|
||||
this.endpointRegistry.registerEndpoint("internal.correlator." + this, endpoint);
|
||||
this.replyMessageCorrelator = correlator;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class ReplyHandlingChannelAdapter implements MessageChannel {
|
||||
|
||||
private final Message<?> originalMessage;
|
||||
|
||||
private final ReplyHandler replyHandler;
|
||||
|
||||
|
||||
ReplyHandlingChannelAdapter(Message<?> originalMessage, ReplyHandler replyHandler) {
|
||||
this.originalMessage = originalMessage;
|
||||
this.replyHandler = replyHandler;
|
||||
}
|
||||
|
||||
|
||||
public List<Message<?>> clear() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public DispatcherPolicy getDispatcherPolicy() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<Message<?>> purge(MessageSelector selector) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
}
|
||||
|
||||
public Message receive() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Message receive(long timeout) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean send(Message<?> message) {
|
||||
this.replyHandler.handle(message, originalMessage.getHeader());
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean send(Message<?> message, long timeout) {
|
||||
return this.send(message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.channel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.message.selector.MessageSelector;
|
||||
|
||||
/**
|
||||
* A channel implementation that stores messages in a thread-bound queue. In
|
||||
* other words, send() will put a message at the tail of the queue for the
|
||||
* current thread, and receive() will retrieve a message from the head of the
|
||||
* queue.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class ThreadLocalChannel extends AbstractMessageChannel {
|
||||
|
||||
private static final ThreadLocalMessageHolder messageHolder = new ThreadLocalMessageHolder();
|
||||
|
||||
|
||||
public ThreadLocalChannel() {
|
||||
super(defaultDispatcherPolicy());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Message<?> doReceive(long timeout) {
|
||||
return messageHolder.get().poll();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doSend(Message<?> message, long timeout) {
|
||||
if (message == null) {
|
||||
return false;
|
||||
}
|
||||
return messageHolder.get().add(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove and return any messages that are stored for the current thread.
|
||||
*/
|
||||
public List<Message<?>> clear() {
|
||||
List<Message<?>> removedMessages = new ArrayList<Message<?>>();
|
||||
Message<?> next = messageHolder.get().poll();
|
||||
while (next != null) {
|
||||
removedMessages.add(next);
|
||||
next = messageHolder.get().poll();
|
||||
}
|
||||
return removedMessages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove and return any messages that are stored for the current thread
|
||||
* and do not match the provided selector.
|
||||
*/
|
||||
public List<Message<?>> purge(MessageSelector selector) {
|
||||
List<Message<?>> removedMessages = new ArrayList<Message<?>>();
|
||||
Object[] allMessages = messageHolder.get().toArray();
|
||||
for (Object next : allMessages) {
|
||||
Message<?> message = (Message<?>) next;
|
||||
if (!selector.accept(message) && messageHolder.get().remove(message)) {
|
||||
removedMessages.add(message);
|
||||
}
|
||||
}
|
||||
return removedMessages;
|
||||
}
|
||||
|
||||
|
||||
private static DispatcherPolicy defaultDispatcherPolicy() {
|
||||
DispatcherPolicy dispatcherPolicy = new DispatcherPolicy(false);
|
||||
dispatcherPolicy.setMaxMessagesPerTask(1);
|
||||
dispatcherPolicy.setReceiveTimeout(0);
|
||||
dispatcherPolicy.setRejectionLimit(1);
|
||||
dispatcherPolicy.setRetryInterval(0);
|
||||
dispatcherPolicy.setShouldFailOnRejectionLimit(false);
|
||||
return dispatcherPolicy;
|
||||
}
|
||||
|
||||
|
||||
private static class ThreadLocalMessageHolder extends ThreadLocal<Queue<Message<?>>> {
|
||||
|
||||
@Override
|
||||
protected Queue<Message<?>> initialValue() {
|
||||
return new LinkedBlockingQueue<Message<?>>();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.channel.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.ManagedList;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.channel.DispatcherPolicy;
|
||||
import org.springframework.integration.channel.interceptor.MessageSelectingInterceptor;
|
||||
import org.springframework.integration.message.selector.PayloadTypeSelector;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Base class for channel parsers.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public abstract class AbstractChannelParser extends AbstractSingleBeanDefinitionParser {
|
||||
|
||||
private static final String PUBLISH_SUBSCRIBE_ATTRIBUTE = "publish-subscribe";
|
||||
|
||||
private static final String DISPATCHER_POLICY_ELEMENT = "dispatcher-policy";
|
||||
|
||||
private static final String DATATYPE_ATTRIBUTE = "datatype";
|
||||
|
||||
private static final String INTERCEPTOR_ELEMENT = "interceptor";
|
||||
|
||||
private static final String INTERCEPTORS_PROPERTY = "interceptors";
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean shouldGenerateId() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldGenerateIdAsFallback() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected abstract Class<?> getBeanClass(Element element);
|
||||
|
||||
protected abstract void configureConstructorArgs(
|
||||
BeanDefinitionBuilder builder, Element element, DispatcherPolicy dispatcherPolicy);
|
||||
|
||||
@Override
|
||||
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||
boolean isPublishSubscribe = "true".equals(element.getAttribute(PUBLISH_SUBSCRIBE_ATTRIBUTE));
|
||||
DispatcherPolicy dispatcherPolicy = new DispatcherPolicy(isPublishSubscribe);
|
||||
ManagedList interceptors = new ManagedList();
|
||||
NodeList childNodes = element.getChildNodes();
|
||||
for (int i = 0; i < childNodes.getLength(); i++) {
|
||||
Node child = childNodes.item(i);
|
||||
if (child.getNodeType() == Node.ELEMENT_NODE) {
|
||||
String localName = child.getLocalName();
|
||||
if (DISPATCHER_POLICY_ELEMENT.equals(localName)) {
|
||||
configureDispatcherPolicy((Element) child, dispatcherPolicy);
|
||||
}
|
||||
else if (INTERCEPTOR_ELEMENT.equals(localName)) {
|
||||
String ref = ((Element) child).getAttribute("ref");
|
||||
interceptors.add(new RuntimeBeanReference(ref));
|
||||
}
|
||||
}
|
||||
}
|
||||
String datatypeAttr = element.getAttribute(DATATYPE_ATTRIBUTE);
|
||||
if (StringUtils.hasText(datatypeAttr)) {
|
||||
String[] datatypes = StringUtils.commaDelimitedListToStringArray(datatypeAttr);
|
||||
RootBeanDefinition selectorDef = new RootBeanDefinition(PayloadTypeSelector.class);
|
||||
selectorDef.getConstructorArgumentValues().addGenericArgumentValue(datatypes);
|
||||
String selectorBeanName = parserContext.getReaderContext().generateBeanName(selectorDef);
|
||||
BeanComponentDefinition selectorComponent = new BeanComponentDefinition(selectorDef, selectorBeanName);
|
||||
parserContext.registerBeanComponent(selectorComponent);
|
||||
RootBeanDefinition interceptorDef = new RootBeanDefinition(MessageSelectingInterceptor.class);
|
||||
interceptorDef.getConstructorArgumentValues().addGenericArgumentValue(new RuntimeBeanReference(selectorBeanName));
|
||||
String interceptorBeanName = parserContext.getReaderContext().generateBeanName(interceptorDef);
|
||||
BeanComponentDefinition interceptorComponent = new BeanComponentDefinition(interceptorDef, interceptorBeanName);
|
||||
parserContext.registerBeanComponent(interceptorComponent);
|
||||
interceptors.add(new RuntimeBeanReference(interceptorBeanName));
|
||||
}
|
||||
builder.addPropertyValue(INTERCEPTORS_PROPERTY, interceptors);
|
||||
this.configureConstructorArgs(builder, element, dispatcherPolicy);
|
||||
}
|
||||
|
||||
private void configureDispatcherPolicy(Element element, DispatcherPolicy dispatcherPolicy) {
|
||||
String maxMessagesPerTask = element.getAttribute("max-messages-per-task");
|
||||
if (StringUtils.hasText(maxMessagesPerTask)) {
|
||||
dispatcherPolicy.setMaxMessagesPerTask(Integer.parseInt(maxMessagesPerTask));
|
||||
}
|
||||
String receiveTimeout = element.getAttribute("receive-timeout");
|
||||
if (StringUtils.hasText(receiveTimeout)) {
|
||||
dispatcherPolicy.setReceiveTimeout(Long.parseLong(receiveTimeout));
|
||||
}
|
||||
String rejectionLimit = element.getAttribute("rejection-limit");
|
||||
if (StringUtils.hasText(rejectionLimit)) {
|
||||
dispatcherPolicy.setRejectionLimit(Integer.parseInt(rejectionLimit));
|
||||
}
|
||||
String retryInterval = element.getAttribute("retry-interval");
|
||||
if (StringUtils.hasText(retryInterval)) {
|
||||
dispatcherPolicy.setRetryInterval(Long.parseLong(retryInterval));
|
||||
}
|
||||
String shouldFailOnRejectionLimit = element.getAttribute("should-fail-on-rejection-limit");
|
||||
if (StringUtils.hasText(shouldFailOnRejectionLimit)) {
|
||||
dispatcherPolicy.setShouldFailOnRejectionLimit("true".equals(shouldFailOnRejectionLimit));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.channel.config;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.integration.channel.DispatcherPolicy;
|
||||
import org.springframework.integration.channel.factory.DefaultChannelFactoryBean;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* Parser for the <channel> element.
|
||||
*
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
public class DefaultChannelParser extends AbstractChannelParser {
|
||||
|
||||
@Override
|
||||
protected Class<?> getBeanClass(Element element) {
|
||||
return DefaultChannelFactoryBean.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureConstructorArgs(BeanDefinitionBuilder builder, Element element, DispatcherPolicy dispatcherPolicy) {
|
||||
builder.addConstructorArgValue(dispatcherPolicy);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.channel.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.channel.DispatcherPolicy;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Parser for the <direct-channel> element.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class DirectChannelParser extends AbstractChannelParser {
|
||||
|
||||
@Override
|
||||
protected Class<?> getBeanClass(Element element) {
|
||||
return DirectChannel.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureConstructorArgs(BeanDefinitionBuilder builder, Element element, DispatcherPolicy dispatcherPolicy) {
|
||||
String source = element.getAttribute("source");
|
||||
if (StringUtils.hasText(source)) {
|
||||
builder.addConstructorArgReference(source);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.channel.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.integration.channel.DispatcherPolicy;
|
||||
import org.springframework.integration.channel.PriorityChannel;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Parser for the <priority-channel> element.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class PriorityChannelParser extends QueueChannelParser {
|
||||
|
||||
@Override
|
||||
protected Class<?> getBeanClass(Element element) {
|
||||
return PriorityChannel.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureConstructorArgs(BeanDefinitionBuilder builder, Element element, DispatcherPolicy dispatcherPolicy) {
|
||||
super.configureConstructorArgs(builder, element, dispatcherPolicy);
|
||||
String comparator = element.getAttribute("comparator");
|
||||
if (StringUtils.hasText(comparator)) {
|
||||
builder.addConstructorArgReference(comparator);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.channel.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.integration.channel.DispatcherPolicy;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Parser for the <queue-channel> element.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class QueueChannelParser extends AbstractChannelParser {
|
||||
|
||||
@Override
|
||||
protected Class<?> getBeanClass(Element element) {
|
||||
return QueueChannel.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureConstructorArgs(BeanDefinitionBuilder builder, Element element, DispatcherPolicy dispatcherPolicy) {
|
||||
String capacityAttribute = element.getAttribute("capacity");
|
||||
int capacity = (StringUtils.hasText(capacityAttribute)) ?
|
||||
Integer.parseInt(capacityAttribute) : QueueChannel.DEFAULT_CAPACITY;
|
||||
builder.addConstructorArgValue(capacity);
|
||||
builder.addConstructorArgValue(dispatcherPolicy);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.channel.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.integration.channel.DispatcherPolicy;
|
||||
import org.springframework.integration.channel.RendezvousChannel;
|
||||
|
||||
/**
|
||||
* Parser for the <rendezvous-channel> element.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class RendezvousChannelParser extends AbstractChannelParser {
|
||||
|
||||
@Override
|
||||
protected Class<?> getBeanClass(Element element) {
|
||||
return RendezvousChannel.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureConstructorArgs(BeanDefinitionBuilder builder, Element element, DispatcherPolicy dispatcherPolicy) {
|
||||
builder.addConstructorArgValue(dispatcherPolicy);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.channel.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.integration.channel.DispatcherPolicy;
|
||||
import org.springframework.integration.channel.ThreadLocalChannel;
|
||||
|
||||
/**
|
||||
* Parser for the <thread-local-channel> element.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class ThreadLocalChannelParser extends AbstractChannelParser {
|
||||
|
||||
@Override
|
||||
protected Class<?> getBeanClass(Element element) {
|
||||
return ThreadLocalChannel.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureConstructorArgs(BeanDefinitionBuilder builder, Element element, DispatcherPolicy dispatcherPolicy) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.channel.factory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.integration.channel.AbstractMessageChannel;
|
||||
import org.springframework.integration.channel.ChannelInterceptor;
|
||||
import org.springframework.integration.channel.DispatcherPolicy;
|
||||
import org.springframework.integration.channel.MessageChannel;
|
||||
|
||||
/**
|
||||
* Base class for {@link ChannelFactory} implementations. Subclasses should
|
||||
* override {@literal createChannelInternal()}.
|
||||
*
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
public abstract class AbstractChannelFactory implements ChannelFactory {
|
||||
|
||||
public AbstractChannelFactory() {
|
||||
super();
|
||||
}
|
||||
|
||||
public final MessageChannel getChannel(DispatcherPolicy dispatcherPolicy, List<ChannelInterceptor> interceptors) {
|
||||
AbstractMessageChannel channel = createChannelInternal(dispatcherPolicy);
|
||||
if (null != interceptors) {
|
||||
channel.setInterceptors(interceptors);
|
||||
}
|
||||
return channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to be overridden by subclasses. It assumes that subclasses will return
|
||||
* subclasses of AbstractMessageChannel.
|
||||
*/
|
||||
protected abstract AbstractMessageChannel createChannelInternal(DispatcherPolicy dispatcherPolicy);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.channel.factory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.integration.channel.ChannelInterceptor;
|
||||
import org.springframework.integration.channel.DispatcherPolicy;
|
||||
import org.springframework.integration.channel.MessageChannel;
|
||||
|
||||
/**
|
||||
* Interface for a channel factory.
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
public interface ChannelFactory {
|
||||
|
||||
/**
|
||||
* Creates a channel, based on the provided dispatcher policy, and with the given interceptors.
|
||||
* @return
|
||||
*/
|
||||
MessageChannel getChannel(DispatcherPolicy dispatcherPolicy, List<ChannelInterceptor> interceptors);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.channel.factory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.integration.bus.MessageBus;
|
||||
import org.springframework.integration.channel.ChannelInterceptor;
|
||||
import org.springframework.integration.channel.DispatcherPolicy;
|
||||
import org.springframework.integration.channel.MessageChannel;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Creates a channel by delegating to the current message bus' configured
|
||||
* ChannelFactory. Tries to retrieve the {@link ChannelFactory} from the
|
||||
* single {@link MessageBus} defined in the {@link ApplicationContext}.
|
||||
* As a {@link FactoryBean}, this class is solely intended to be used within
|
||||
* an ApplicationContext.
|
||||
*
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
public class DefaultChannelFactoryBean implements ApplicationContextAware, FactoryBean{
|
||||
|
||||
private volatile ChannelFactory channelFactory;
|
||||
|
||||
private volatile List<ChannelInterceptor> interceptors;
|
||||
|
||||
private volatile DispatcherPolicy dispatcherPolicy;
|
||||
|
||||
|
||||
public DefaultChannelFactoryBean(DispatcherPolicy dispatcherPolicy) {
|
||||
this.dispatcherPolicy = dispatcherPolicy;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setApplicationContext(ApplicationContext applicationContext){
|
||||
Map map = applicationContext.getBeansOfType(MessageBus.class);
|
||||
Assert.state(map.size() <= 1, "There is more than one MessageBus in the ApplicationContext");
|
||||
if (map.isEmpty()) {
|
||||
this.channelFactory = new QueueChannelFactory();
|
||||
}
|
||||
else {
|
||||
this.channelFactory = ((MessageBus) map.values().iterator().next()).getChannelFactory();
|
||||
}
|
||||
}
|
||||
|
||||
public void setInterceptors(List<ChannelInterceptor> interceptors) {
|
||||
this.interceptors = interceptors;
|
||||
}
|
||||
|
||||
public Object getObject() throws Exception {
|
||||
Assert.notNull(channelFactory, "ChannelFactory not set on this instance. Is this used within an ApplicationContext?");
|
||||
return channelFactory.getChannel(dispatcherPolicy, interceptors);
|
||||
}
|
||||
|
||||
public Class<?> getObjectType() {
|
||||
return MessageChannel.class;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.channel.factory;
|
||||
|
||||
import org.springframework.integration.channel.AbstractMessageChannel;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.channel.DispatcherPolicy;
|
||||
|
||||
/**
|
||||
* A {@link ChannelFactory} for creating {@link DirectChannel} instances.
|
||||
*
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
public class DirectChannelFactory extends AbstractChannelFactory {
|
||||
|
||||
@Override
|
||||
protected AbstractMessageChannel createChannelInternal(DispatcherPolicy dispatcherPolicy) {
|
||||
return new DirectChannel(null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.channel.factory;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
import org.springframework.integration.channel.AbstractMessageChannel;
|
||||
import org.springframework.integration.channel.DispatcherPolicy;
|
||||
import org.springframework.integration.channel.PriorityChannel;
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link ChannelFactory} for creating {@link PriorityChannel} instances.
|
||||
* @author Marius Bogoevici
|
||||
*
|
||||
*/
|
||||
public class PriorityChannelFactory extends AbstractChannelFactory {
|
||||
|
||||
private int capacity = PriorityChannel.DEFAULT_CAPACITY;
|
||||
|
||||
private Comparator<Message<?>> comparator;
|
||||
|
||||
|
||||
public void setCapacity(int capacity) {
|
||||
Assert.isTrue(capacity > 0, "capacity must be a positive value");
|
||||
this.capacity = capacity;
|
||||
}
|
||||
|
||||
public void setComparator(Comparator<Message<?>> comparator) {
|
||||
this.comparator = comparator;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractMessageChannel createChannelInternal(DispatcherPolicy dispatcherPolicy) {
|
||||
return new PriorityChannel(this.capacity, dispatcherPolicy, this.comparator);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.channel.factory;
|
||||
|
||||
|
||||
import org.springframework.integration.channel.AbstractMessageChannel;
|
||||
import org.springframework.integration.channel.DispatcherPolicy;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Default implementation of a {@link ChannelFactory}, which will create instances of a
|
||||
* {@link QueueChannel}.
|
||||
*
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
public class QueueChannelFactory extends AbstractChannelFactory{
|
||||
|
||||
int queueCapacity = QueueChannel.DEFAULT_CAPACITY;
|
||||
|
||||
public int getQueueCapacity() {
|
||||
return queueCapacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the queue capacity for the newly created channels.
|
||||
* By default, the queue capacity is {@literal QueueChannel.DEFAULT_CAPACITY}
|
||||
* @param queueCapacity
|
||||
*/
|
||||
public void setQueueCapacity(int queueCapacity) {
|
||||
Assert.state(queueCapacity > 0, "Queue capacity must be greater than zero");
|
||||
this.queueCapacity = queueCapacity;
|
||||
}
|
||||
|
||||
protected AbstractMessageChannel createChannelInternal(DispatcherPolicy dispatcherPolicy) {
|
||||
return new QueueChannel(queueCapacity, dispatcherPolicy);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.channel.factory;
|
||||
|
||||
import org.springframework.integration.channel.AbstractMessageChannel;
|
||||
import org.springframework.integration.channel.DispatcherPolicy;
|
||||
import org.springframework.integration.channel.RendezvousChannel;
|
||||
|
||||
/**
|
||||
* A {@link ChannelFactory} for creating {@link RendezvousChannel} instances.
|
||||
*
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
public class RendezvousChannelFactory extends AbstractChannelFactory {
|
||||
|
||||
@Override
|
||||
protected AbstractMessageChannel createChannelInternal(DispatcherPolicy dispatcherPolicy) {
|
||||
return new RendezvousChannel(dispatcherPolicy);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.channel.factory;
|
||||
|
||||
import org.springframework.integration.channel.AbstractMessageChannel;
|
||||
import org.springframework.integration.channel.DispatcherPolicy;
|
||||
import org.springframework.integration.channel.ThreadLocalChannel;
|
||||
|
||||
/**
|
||||
* A {@link ChannelFactory} implementation for creating {@link ThreadLocalChannel} instances.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class ThreadLocalChannelFactory extends AbstractChannelFactory {
|
||||
|
||||
@Override
|
||||
protected AbstractMessageChannel createChannelInternal(DispatcherPolicy dispatcherPolicy) {
|
||||
return new ThreadLocalChannel();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.channel.interceptor;
|
||||
|
||||
import org.springframework.integration.channel.ChannelInterceptor;
|
||||
import org.springframework.integration.channel.MessageChannel;
|
||||
import org.springframework.integration.message.Message;
|
||||
|
||||
/**
|
||||
* A {@link ChannelInterceptor} with no-op method implementations so that
|
||||
* subclasses do not have to implement all of the interface's methods.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class ChannelInterceptorAdapter implements ChannelInterceptor {
|
||||
|
||||
public boolean preSend(Message<?> message, MessageChannel channel) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void postSend(Message<?> message, MessageChannel channel, boolean sent) {
|
||||
}
|
||||
|
||||
public boolean preReceive(MessageChannel channel) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void postReceive(Message<?> message, MessageChannel channel) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.channel.interceptor;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.integration.channel.MessageChannel;
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.message.MessageDeliveryException;
|
||||
import org.springframework.integration.message.selector.MessageSelector;
|
||||
|
||||
/**
|
||||
* A {@link org.springframework.integration.channel.ChannelInterceptor} that
|
||||
* delegates to a list of {@link MessageSelector MessageSelectors} to decide
|
||||
* whether a {@link Message} should be accepted on the {@link MessageChannel}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class MessageSelectingInterceptor extends ChannelInterceptorAdapter {
|
||||
|
||||
private final List<MessageSelector> selectors;
|
||||
|
||||
|
||||
public MessageSelectingInterceptor(MessageSelector... selectors) {
|
||||
this.selectors = Arrays.asList(selectors);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean preSend(Message<?> message, MessageChannel channel) {
|
||||
for (MessageSelector selector : this.selectors) {
|
||||
if (!selector.accept(message)) {
|
||||
throw new MessageDeliveryException(message,
|
||||
"selector '" + selector + "' did not accept message");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.channel.interceptor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.integration.channel.ChannelInterceptor;
|
||||
import org.springframework.integration.channel.MessageChannel;
|
||||
import org.springframework.integration.message.GenericMessage;
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.message.selector.MessageSelector;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link ChannelInterceptor} that publishes a copy of the intercepted message
|
||||
* to a secondary channel while still sending the original message to the main channel.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class WireTap extends ChannelInterceptorAdapter implements Lifecycle {
|
||||
|
||||
/** key for the attribute containing the original Message's id */
|
||||
public final static String ORIGINAL_MESSAGE_ID_KEY = "_wireTap.originalMessageId";
|
||||
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
private final MessageChannel secondaryChannel;
|
||||
|
||||
private final List<MessageSelector> selectors = new CopyOnWriteArrayList<MessageSelector>();
|
||||
|
||||
private volatile boolean running = true;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new wire tap with <em>no</em> {@link MessageSelector MessageSelectors}.
|
||||
*
|
||||
* @param secondaryChannel the channel to which duplicate messages will be sent
|
||||
*/
|
||||
public WireTap(MessageChannel secondaryChannel) {
|
||||
Assert.notNull(secondaryChannel, "'secondaryChannel' must not be null");
|
||||
this.secondaryChannel = secondaryChannel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new wire tap with {@link MessageSelector MessageSelectors}.
|
||||
*
|
||||
* @param secondaryChannel the channel to which duplicate messages will be sent
|
||||
* @param selectors the list of selectors that must accept a message for it to
|
||||
* be sent to the secondary channel
|
||||
*/
|
||||
public WireTap(MessageChannel secondaryChannel, List<MessageSelector> selectors) {
|
||||
this(secondaryChannel);
|
||||
if (selectors != null) {
|
||||
this.selectors.addAll(selectors);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check whether the wire tap is currently running.
|
||||
*/
|
||||
public boolean isRunning() {
|
||||
return this.running;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restart the wire tap if it has been stopped. It is running by default.
|
||||
*/
|
||||
public void start() {
|
||||
this.running = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the wire tap. To restart, invoke {@link #start()}.
|
||||
*/
|
||||
public void stop() {
|
||||
this.running = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean preSend(Message<?> message, MessageChannel channel) {
|
||||
if (this.running && this.selectorsAccept(message)) {
|
||||
Message<?> duplicate = new GenericMessage<Object>(message.getPayload(), message.getHeader());
|
||||
duplicate.getHeader().setAttribute(ORIGINAL_MESSAGE_ID_KEY, message.getId());
|
||||
if (!this.secondaryChannel.send(duplicate, 0)) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Failed to send message to secondary channel '" + this.secondaryChannel.getName()
|
||||
+ "'. Check its capacity and whether it has any subscribers.");
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this wire tap has any {@link MessageSelector MessageSelectors}, check
|
||||
* whether they accept the current message. If any of them do not accept it,
|
||||
* the message will <em>not</em> be sent to the secondary channel.
|
||||
*/
|
||||
private boolean selectorsAccept(Message<?> message) {
|
||||
for (MessageSelector selector : this.selectors) {
|
||||
if (!selector.accept(message)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.ManagedList;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.ConfigurationException;
|
||||
import org.springframework.integration.endpoint.ConcurrencyPolicy;
|
||||
import org.springframework.integration.scheduling.PollingSchedule;
|
||||
import org.springframework.integration.scheduling.Schedule;
|
||||
import org.springframework.integration.scheduling.Subscription;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Base class for <em>target-endpoint</em> and <em>handler-endpoint</em> parsers.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public abstract class AbstractTargetEndpointParser extends AbstractSingleBeanDefinitionParser {
|
||||
|
||||
private static final String INPUT_CHANNEL_ATTRIBUTE = "input-channel";
|
||||
|
||||
private static final String SUBSCRIPTION_PROPERTY = "subscription";
|
||||
|
||||
private static final String SELECTOR_ELEMENT = "selector";
|
||||
|
||||
private static final String REF_ATTRIBUTE = "ref";
|
||||
|
||||
private static final String SELECTORS_PROPERTY = "messageSelectors";
|
||||
|
||||
private static final String ERROR_HANDLER_ATTRIBUTE = "error-handler";
|
||||
|
||||
private static final String ERROR_HANDLER_PROPERTY = "errorHandler";
|
||||
|
||||
private static final String PERIOD_ATTRIBUTE = "period";
|
||||
|
||||
private static final String SCHEDULE_ELEMENT = "schedule";
|
||||
|
||||
private static final String CONCURRENCY_ELEMENT = "concurrency";
|
||||
|
||||
private static final String CONCURRENCY_POLICY_PROPERTY = "concurrencyPolicy";
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean shouldGenerateId() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldGenerateIdAsFallback() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected abstract Class<?> getBeanClass(Element element);
|
||||
|
||||
protected abstract String getTargetAttributeName();
|
||||
|
||||
protected abstract Class<?> getAdapterClass();
|
||||
|
||||
protected void postProcess(BeanDefinitionBuilder builder, Element element) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||
this.parseTarget(element, this.getTargetAttributeName(), parserContext, builder);
|
||||
String inputChannel = element.getAttribute(INPUT_CHANNEL_ATTRIBUTE);
|
||||
Schedule schedule = null;
|
||||
ManagedList selectors = new ManagedList();
|
||||
NodeList childNodes = element.getChildNodes();
|
||||
for (int i = 0; i < childNodes.getLength(); i++) {
|
||||
Node child = childNodes.item(i);
|
||||
if (child.getNodeType() == Node.ELEMENT_NODE) {
|
||||
String localName = child.getLocalName();
|
||||
if (CONCURRENCY_ELEMENT.equals(localName)) {
|
||||
parseConcurrencyPolicy((Element) child, builder);
|
||||
}
|
||||
else if (SELECTOR_ELEMENT.equals(localName)) {
|
||||
String ref = ((Element) child).getAttribute(REF_ATTRIBUTE);
|
||||
selectors.add(new RuntimeBeanReference(ref));
|
||||
}
|
||||
else if (SCHEDULE_ELEMENT.equals(localName)) {
|
||||
schedule = this.parseSchedule((Element) child);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (StringUtils.hasText(inputChannel)) {
|
||||
RootBeanDefinition subscriptionDef = new RootBeanDefinition(Subscription.class);
|
||||
subscriptionDef.getConstructorArgumentValues().addGenericArgumentValue(inputChannel);
|
||||
if (schedule != null) {
|
||||
subscriptionDef.getConstructorArgumentValues().addGenericArgumentValue(schedule);
|
||||
}
|
||||
String subscriptionBeanName = parserContext.getReaderContext().generateBeanName(subscriptionDef);
|
||||
parserContext.registerBeanComponent(new BeanComponentDefinition(subscriptionDef, subscriptionBeanName));
|
||||
builder.addPropertyReference(SUBSCRIPTION_PROPERTY, subscriptionBeanName);
|
||||
}
|
||||
if (selectors.size() > 0) {
|
||||
builder.addPropertyValue(SELECTORS_PROPERTY, selectors);
|
||||
}
|
||||
String errorHandlerRef = element.getAttribute(ERROR_HANDLER_ATTRIBUTE);
|
||||
if (StringUtils.hasText(errorHandlerRef)) {
|
||||
builder.addPropertyReference(ERROR_HANDLER_PROPERTY, errorHandlerRef);
|
||||
}
|
||||
this.postProcess(builder, element);
|
||||
}
|
||||
|
||||
private void parseTarget(Element element, String attribute, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||
String ref = element.getAttribute(attribute);
|
||||
if (!StringUtils.hasText(ref)) {
|
||||
throw new ConfigurationException("The '" + attribute + "' attribute is required.");
|
||||
}
|
||||
String method = element.getAttribute("method");
|
||||
if (StringUtils.hasText(method)) {
|
||||
String adapterBeanName = this.parseAdapter(ref, method, parserContext);
|
||||
builder.addConstructorArgReference(adapterBeanName);
|
||||
}
|
||||
else {
|
||||
builder.addConstructorArgReference(ref);
|
||||
}
|
||||
}
|
||||
|
||||
private String parseAdapter(String ref, String method, ParserContext parserContext) {
|
||||
BeanDefinition adapterDef = new RootBeanDefinition(this.getAdapterClass());
|
||||
adapterDef.getPropertyValues().addPropertyValue("object", new RuntimeBeanReference(ref));
|
||||
adapterDef.getPropertyValues().addPropertyValue("methodName", method);
|
||||
String adapterBeanName = parserContext.getReaderContext().generateBeanName(adapterDef);
|
||||
parserContext.registerBeanComponent(new BeanComponentDefinition(adapterDef, adapterBeanName));
|
||||
return adapterBeanName;
|
||||
}
|
||||
|
||||
private void parseConcurrencyPolicy(Element concurrencyElement, BeanDefinitionBuilder builder) {
|
||||
ConcurrencyPolicy policy = IntegrationNamespaceUtils.parseConcurrencyPolicy(concurrencyElement);
|
||||
builder.addPropertyValue(CONCURRENCY_POLICY_PROPERTY, policy);
|
||||
}
|
||||
|
||||
private Schedule parseSchedule(Element scheduleElement) {
|
||||
PollingSchedule schedule = new PollingSchedule(5);
|
||||
String period = scheduleElement.getAttribute(PERIOD_ATTRIBUTE);
|
||||
if (StringUtils.hasText(period)) {
|
||||
schedule.setPeriod(Integer.parseInt(period));
|
||||
}
|
||||
return schedule;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.ConfigurationException;
|
||||
import org.springframework.integration.router.AggregatingMessageHandler;
|
||||
import org.springframework.integration.router.AggregatorAdapter;
|
||||
import org.springframework.integration.router.CompletionStrategyAdapter;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Parser for the <em>aggregator</em> element of the integration namespace.
|
||||
* Registers the annotation-driven post-processors.
|
||||
*
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
public class AggregatorParser implements BeanDefinitionParser {
|
||||
|
||||
public static final String ID_ATTRIBUTE = "id";
|
||||
|
||||
public static final String REF_ATTRIBUTE = "ref";
|
||||
|
||||
public static final String METHOD_ATTRIBUTE = "method";
|
||||
|
||||
public static final String COMPLETION_STRATEGY_ATTRIBUTE = "completion-strategy";
|
||||
|
||||
public static final String DEFAULT_REPLY_CHANNEL_ATTRIBUTE = "default-reply-channel";
|
||||
|
||||
public static final String DISCARD_CHANNEL_ATTRIBUTE = "discard-channel";
|
||||
|
||||
public static final String SEND_TIMEOUT_ATTRIBUTE = "send-timeout";
|
||||
|
||||
public static final String SEND_PARTIAL_RESULT_ON_TIMEOUT_ATTRIBUTE = "send-partial-result-on-timeout";
|
||||
|
||||
public static final String REAPER_INTERVAL_ATTRIBUTE = "reaper-interval";
|
||||
|
||||
public static final String TRACKED_CORRELATION_ID_CAPACITY_ATTRIBUTE = "tracked-correlation-id-capacity";
|
||||
|
||||
public static final String TIMEOUT_ATTRIBUTE = "timeout";
|
||||
|
||||
private static final String COMPLETION_STRATEGY_PROPERTY = "completionStrategy";
|
||||
|
||||
private static final String DEFAULT_REPLY_CHANNEL_PROPERTY = "defaultReplyChannel";
|
||||
|
||||
private static final String DISCARD_CHANNEL_PROPERTY = "discardChannel";
|
||||
|
||||
private static final String SEND_TIMEOUT_PROPERTY = "sendTimeout";
|
||||
|
||||
private static final String SEND_PARTIAL_RESULT_ON_TIMEOUT_PROPERTY = "sendPartialResultOnTimeout";
|
||||
|
||||
private static final String REAPER_INTERVAL_PROPERTY = "reaperInterval";
|
||||
|
||||
public static final String TRACKED_CORRELATION_ID_CAPACITY_PROPERTY = "trackedCorrelationIdCapacity";
|
||||
|
||||
public static final String TIMEOUT = "timeout";
|
||||
|
||||
public static final String AGGREGATOR_ELEMENT = "aggregator";
|
||||
|
||||
public static final String COMPLETION_STRATEGY_ELEMENT = "completion-strategy";
|
||||
|
||||
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
return parseAggregatorElement(element, parserContext, true);
|
||||
}
|
||||
|
||||
private BeanDefinition parseAggregatorElement(Element element, ParserContext parserContext, boolean topLevel) {
|
||||
final RootBeanDefinition aggregatorDef = new RootBeanDefinition(AggregatingMessageHandler.class);
|
||||
aggregatorDef.setSource(parserContext.extractSource(element));
|
||||
final String id = element.getAttribute(ID_ATTRIBUTE);
|
||||
final String ref = element.getAttribute(REF_ATTRIBUTE);
|
||||
final String method = element.getAttribute(METHOD_ATTRIBUTE);
|
||||
final String completionStrategyRef = element.getAttribute(COMPLETION_STRATEGY_ATTRIBUTE);
|
||||
final NodeList completionStrategyChildElements = element.getElementsByTagName(COMPLETION_STRATEGY_ELEMENT);
|
||||
if (!StringUtils.hasText(ref)) {
|
||||
throw new ConfigurationException("The 'ref' attribute must be present");
|
||||
}
|
||||
if (!topLevel && StringUtils.hasText(id)) {
|
||||
parserContext.getReaderContext().error(
|
||||
"The 'id' attribute is only supported for top-level <aggregator> elements.",
|
||||
parserContext.extractSource(element));
|
||||
}
|
||||
if (completionStrategyChildElements.getLength() > 0 && StringUtils.hasText(completionStrategyRef)) {
|
||||
parserContext
|
||||
.getReaderContext()
|
||||
.error(
|
||||
"The 'completion-strategy' element is only supported when no 'completion-strategy' attribute is specified.",
|
||||
parserContext.extractSource(element));
|
||||
}
|
||||
if (!StringUtils.hasText(method)) {
|
||||
aggregatorDef.getConstructorArgumentValues().addGenericArgumentValue(new RuntimeBeanReference(ref));
|
||||
}
|
||||
else {
|
||||
String adapterBeanName = createAdapterAndReturnBeanName(parserContext, ref, method, AggregatorAdapter.class);
|
||||
aggregatorDef.getConstructorArgumentValues().addGenericArgumentValue(
|
||||
new RuntimeBeanReference(adapterBeanName));
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(completionStrategyRef)) {
|
||||
aggregatorDef.getPropertyValues().addPropertyValue(COMPLETION_STRATEGY_PROPERTY,
|
||||
new RuntimeBeanReference(completionStrategyRef));
|
||||
}
|
||||
else if (completionStrategyChildElements.getLength() > 0) {
|
||||
Element completionStrategyElement = (Element) completionStrategyChildElements.item(0);
|
||||
String childCompletionStrategyReference = completionStrategyElement.getAttribute(REF_ATTRIBUTE);
|
||||
String childCompletionStrategyMethod = completionStrategyElement.getAttribute(METHOD_ATTRIBUTE);
|
||||
String adapterBeanName = createAdapterAndReturnBeanName(parserContext, childCompletionStrategyReference,
|
||||
childCompletionStrategyMethod, CompletionStrategyAdapter.class);
|
||||
aggregatorDef.getPropertyValues().addPropertyValue(COMPLETION_STRATEGY_PROPERTY,
|
||||
new RuntimeBeanReference(adapterBeanName));
|
||||
}
|
||||
|
||||
IntegrationNamespaceUtils.setBeanReferenceIfAttributeDefined(aggregatorDef, DEFAULT_REPLY_CHANNEL_PROPERTY,
|
||||
element, DEFAULT_REPLY_CHANNEL_ATTRIBUTE);
|
||||
IntegrationNamespaceUtils.setBeanReferenceIfAttributeDefined(aggregatorDef, DISCARD_CHANNEL_PROPERTY, element,
|
||||
DISCARD_CHANNEL_ATTRIBUTE);
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(aggregatorDef, SEND_TIMEOUT_PROPERTY, element,
|
||||
SEND_TIMEOUT_ATTRIBUTE);
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(aggregatorDef, SEND_PARTIAL_RESULT_ON_TIMEOUT_PROPERTY,
|
||||
element, SEND_PARTIAL_RESULT_ON_TIMEOUT_ATTRIBUTE);
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(aggregatorDef, REAPER_INTERVAL_PROPERTY, element,
|
||||
REAPER_INTERVAL_ATTRIBUTE);
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(aggregatorDef, TRACKED_CORRELATION_ID_CAPACITY_PROPERTY,
|
||||
element, TRACKED_CORRELATION_ID_CAPACITY_ATTRIBUTE);
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(aggregatorDef, TIMEOUT, element, TIMEOUT_ATTRIBUTE);
|
||||
String beanName = StringUtils.hasText(id) ? id : parserContext.getReaderContext().generateBeanName(
|
||||
aggregatorDef);
|
||||
parserContext.registerBeanComponent(new BeanComponentDefinition(aggregatorDef, beanName));
|
||||
return aggregatorDef;
|
||||
}
|
||||
|
||||
private String createAdapterAndReturnBeanName(ParserContext parserContext, final String ref, final String method,
|
||||
Class<?> adapterClass) {
|
||||
BeanDefinition adapterDefinition = new RootBeanDefinition(adapterClass);
|
||||
adapterDefinition.getConstructorArgumentValues().addGenericArgumentValue(new RuntimeBeanReference(ref));
|
||||
adapterDefinition.getConstructorArgumentValues().addGenericArgumentValue(method);
|
||||
String adapterBeanName = parserContext.getReaderContext().generateBeanName(adapterDefinition);
|
||||
parserContext.registerBeanComponent(new BeanComponentDefinition(adapterDefinition, adapterBeanName));
|
||||
return adapterBeanName;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
|
||||
/**
|
||||
* Parser for the <em>annotation-driven</em> element of the integration
|
||||
* namespace. Registers the annotation-driven post-processors.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class AnnotationDrivenParser implements BeanDefinitionParser {
|
||||
|
||||
private static final String PUBLISHER_ANNOTATION_POST_PROCESSOR_BEAN_NAME =
|
||||
"internal.PublisherAnnotationPostProcessor";
|
||||
|
||||
private static final String SUBSCRIBER_ANNOTATION_POST_PROCESSOR_BEAN_NAME =
|
||||
"internal.SubscriberAnnotationPostProcessor";
|
||||
|
||||
private static final String MESSAGE_ENDPOINT_ANNOTATION_POST_PROCESSOR_BEAN_NAME =
|
||||
"internal.MessageEndpointAnnotationPostProcessor";
|
||||
|
||||
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
this.createPublisherPostProcessor(parserContext);
|
||||
this.createSubscriberPostProcessor(parserContext);
|
||||
this.createMessageEndpointPostProcessor(parserContext);
|
||||
return null;
|
||||
}
|
||||
|
||||
private void createPublisherPostProcessor(ParserContext parserContext) {
|
||||
BeanDefinition bd = new RootBeanDefinition(PublisherAnnotationPostProcessor.class);
|
||||
bd.getPropertyValues().addPropertyValue("channelRegistry",
|
||||
new RuntimeBeanReference(MessageBusParser.MESSAGE_BUS_BEAN_NAME));
|
||||
BeanComponentDefinition bcd = new BeanComponentDefinition(
|
||||
bd, PUBLISHER_ANNOTATION_POST_PROCESSOR_BEAN_NAME);
|
||||
parserContext.registerBeanComponent(bcd);
|
||||
}
|
||||
|
||||
private void createSubscriberPostProcessor(ParserContext parserContext) {
|
||||
BeanDefinition bd = new RootBeanDefinition(SubscriberAnnotationPostProcessor.class);
|
||||
bd.getPropertyValues().addPropertyValue("messageBus",
|
||||
new RuntimeBeanReference(MessageBusParser.MESSAGE_BUS_BEAN_NAME));
|
||||
BeanComponentDefinition bcd = new BeanComponentDefinition(
|
||||
bd, SUBSCRIBER_ANNOTATION_POST_PROCESSOR_BEAN_NAME);
|
||||
parserContext.registerBeanComponent(bcd);
|
||||
}
|
||||
|
||||
private void createMessageEndpointPostProcessor(ParserContext parserContext) {
|
||||
BeanDefinition bd = new RootBeanDefinition(MessageEndpointAnnotationPostProcessor.class);
|
||||
bd.getConstructorArgumentValues().addGenericArgumentValue(
|
||||
new RuntimeBeanReference(MessageBusParser.MESSAGE_BUS_BEAN_NAME));
|
||||
BeanComponentDefinition bcd = new BeanComponentDefinition(
|
||||
bd, MESSAGE_ENDPOINT_ANNOTATION_POST_PROCESSOR_BEAN_NAME);
|
||||
parserContext.registerBeanComponent(bcd);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.integration.endpoint.HandlerEndpoint;
|
||||
import org.springframework.integration.handler.DefaultMessageHandlerAdapter;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Parser for the <em>handler-endpoint</em> element of the integration namespace.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class HandlerEndpointParser extends AbstractTargetEndpointParser {
|
||||
|
||||
private static final String OUTPUT_CHANNEL_ATTRIBUTE = "output-channel";
|
||||
|
||||
private static final String OUTPUT_CHANNEL_PROPERTY = "outputChannelName";
|
||||
|
||||
private static final String RETURN_ADDRESS_OVERRIDES_ATTRIBUTE = "return-address-overrides";
|
||||
|
||||
private static final String REPLY_HANDLER_ATTRIBUTE = "reply-handler";
|
||||
|
||||
private static final String REPLY_HANDLER_PROPERTY = "replyHandler";
|
||||
|
||||
|
||||
@Override
|
||||
protected Class<?> getBeanClass(Element element) {
|
||||
return HandlerEndpoint.class;
|
||||
}
|
||||
|
||||
protected String getTargetAttributeName() {
|
||||
return "handler";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> getAdapterClass() {
|
||||
return DefaultMessageHandlerAdapter.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postProcess(BeanDefinitionBuilder builder, Element element) {
|
||||
String outputChannel = element.getAttribute(OUTPUT_CHANNEL_ATTRIBUTE);
|
||||
if (StringUtils.hasText(outputChannel)) {
|
||||
builder.addPropertyValue(OUTPUT_CHANNEL_PROPERTY, outputChannel);
|
||||
}
|
||||
String returnAddressOverridesAttribute = element.getAttribute(RETURN_ADDRESS_OVERRIDES_ATTRIBUTE);
|
||||
boolean returnAddressOverrides = "true".equals(returnAddressOverridesAttribute);
|
||||
builder.addPropertyValue("returnAddressOverrides", returnAddressOverrides);
|
||||
String replyHandler = element.getAttribute(REPLY_HANDLER_ATTRIBUTE);
|
||||
if (StringUtils.hasText(replyHandler)) {
|
||||
builder.addPropertyValue(REPLY_HANDLER_PROPERTY, new RuntimeBeanReference(replyHandler));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.support.ManagedList;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.handler.DefaultMessageHandlerAdapter;
|
||||
import org.springframework.integration.handler.MessageHandlerChain;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Parser for the <handler/> element.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class HandlerParser implements BeanDefinitionParser {
|
||||
|
||||
private static final String HANDLER_CHAIN_ELEMENT = "handler-chain";
|
||||
|
||||
private static final String HANDLER_ELEMENT = "handler";
|
||||
|
||||
private static final String HANDLERS_PROPERTY = "handlers";
|
||||
|
||||
private static final String OBJECT_PROPERTY = "object";
|
||||
|
||||
private static final String METHOD_NAME_PROPERTY = "methodName";
|
||||
|
||||
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
if (HANDLER_CHAIN_ELEMENT.equals(element.getLocalName())) {
|
||||
return this.parseHandlerChain(element, parserContext);
|
||||
}
|
||||
else if (HANDLER_ELEMENT.equals(element.getLocalName())) {
|
||||
return this.parseHandler(element, parserContext, null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private BeanDefinition parseHandlerChain(Element element, ParserContext parserContext) {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(MessageHandlerChain.class);
|
||||
ManagedList handlers = new ManagedList();
|
||||
NodeList childNodes = element.getChildNodes();
|
||||
for (int i = 0; i < childNodes.getLength(); i++) {
|
||||
Node child = childNodes.item(i);
|
||||
if (child.getNodeType() == Node.ELEMENT_NODE) {
|
||||
String localName = child.getLocalName();
|
||||
if (HANDLER_ELEMENT.equals(localName)) {
|
||||
parseHandler((Element) child, parserContext, handlers);
|
||||
}
|
||||
}
|
||||
}
|
||||
beanDefinition.getPropertyValues().addPropertyValue(HANDLERS_PROPERTY, handlers);
|
||||
String id = element.getAttribute("id");
|
||||
String beanName = (StringUtils.hasText(id)) ? id : parserContext.getReaderContext().generateBeanName(beanDefinition);
|
||||
parserContext.registerBeanComponent(new BeanComponentDefinition(beanDefinition, beanName));
|
||||
return beanDefinition;
|
||||
}
|
||||
|
||||
private BeanDefinition parseHandler(Element element, ParserContext parserContext, ManagedList handlers) {
|
||||
boolean isInnerHandler = (handlers != null);
|
||||
String ref = element.getAttribute("ref");
|
||||
String method = element.getAttribute("method");
|
||||
String id = element.getAttribute("id");
|
||||
if (!isInnerHandler && (!StringUtils.hasText(id) || !StringUtils.hasText(ref) || !StringUtils.hasText(method))) {
|
||||
parserContext.getReaderContext().error("Top-level <handler> elements must provide 'id', 'ref', and 'method' attributes.",
|
||||
parserContext.extractSource(element));
|
||||
}
|
||||
if (isInnerHandler && StringUtils.hasText(id)) {
|
||||
parserContext.getReaderContext().error("The 'id' attribute is only supported for top-level <handler> elements.",
|
||||
parserContext.extractSource(element));
|
||||
}
|
||||
if (StringUtils.hasText(method)) {
|
||||
BeanDefinitionHolder bdh = this.parseHandlerAdapter(id, ref, method, parserContext, isInnerHandler);
|
||||
if (handlers != null) {
|
||||
handlers.add(bdh.getBeanDefinition());
|
||||
return null;
|
||||
}
|
||||
return bdh.getBeanDefinition();
|
||||
}
|
||||
if (StringUtils.hasText(id)) {
|
||||
parserContext.getReaderContext().error("The 'id' attribute is only supported for handler adapters (when 'method' is also provided).",
|
||||
parserContext.extractSource(element));
|
||||
}
|
||||
if (handlers != null) {
|
||||
handlers.add(new RuntimeBeanReference(ref));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private BeanDefinitionHolder parseHandlerAdapter(String id, String handlerRef, String handlerMethod, ParserContext parserContext, boolean isInnerHandler) {
|
||||
BeanDefinition handlerAdapterDef = new RootBeanDefinition(DefaultMessageHandlerAdapter.class);
|
||||
handlerAdapterDef.getPropertyValues().addPropertyValue(OBJECT_PROPERTY, new RuntimeBeanReference(handlerRef));
|
||||
handlerAdapterDef.getPropertyValues().addPropertyValue(METHOD_NAME_PROPERTY, handlerMethod);
|
||||
String adapterBeanName = (StringUtils.hasText(id)) ? id :
|
||||
BeanDefinitionReaderUtils.generateBeanName(handlerAdapterDef, parserContext.getRegistry(), isInnerHandler);
|
||||
if (!isInnerHandler) {
|
||||
parserContext.registerBeanComponent(new BeanComponentDefinition(handlerAdapterDef, adapterBeanName));
|
||||
}
|
||||
return new BeanDefinitionHolder(handlerAdapterDef, adapterBeanName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.config;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
|
||||
import org.springframework.core.io.support.PropertiesLoaderUtils;
|
||||
import org.springframework.integration.channel.config.DefaultChannelParser;
|
||||
import org.springframework.integration.channel.config.DirectChannelParser;
|
||||
import org.springframework.integration.channel.config.PriorityChannelParser;
|
||||
import org.springframework.integration.channel.config.QueueChannelParser;
|
||||
import org.springframework.integration.channel.config.RendezvousChannelParser;
|
||||
import org.springframework.integration.channel.config.ThreadLocalChannelParser;
|
||||
import org.springframework.integration.router.config.RouterParser;
|
||||
import org.springframework.integration.router.config.SplitterParser;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Namespace handler for the integration namespace.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
public class IntegrationNamespaceHandler extends NamespaceHandlerSupport {
|
||||
|
||||
private static final String ADAPTER_PARSER_MAPPINGS_LOCATION = "META-INF/spring-integration.parsers";
|
||||
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
|
||||
public void init() {
|
||||
registerBeanDefinitionParser("message-bus", new MessageBusParser());
|
||||
registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenParser());
|
||||
registerBeanDefinitionParser("channel", new DefaultChannelParser());
|
||||
registerBeanDefinitionParser("queue-channel", new QueueChannelParser());
|
||||
registerBeanDefinitionParser("direct-channel", new DirectChannelParser());
|
||||
registerBeanDefinitionParser("priority-channel", new PriorityChannelParser());
|
||||
registerBeanDefinitionParser("rendezvous-channel", new RendezvousChannelParser());
|
||||
registerBeanDefinitionParser("thread-local-channel", new ThreadLocalChannelParser());
|
||||
registerBeanDefinitionParser("source-adapter", new MethodInvokingAdapterParser());
|
||||
registerBeanDefinitionParser("target-adapter", new MethodInvokingAdapterParser());
|
||||
registerBeanDefinitionParser("source-endpoint", new SourceEndpointParser());
|
||||
registerBeanDefinitionParser("handler-endpoint", new HandlerEndpointParser());
|
||||
registerBeanDefinitionParser("target-endpoint", new TargetEndpointParser());
|
||||
registerBeanDefinitionParser("handler", new HandlerParser());
|
||||
registerBeanDefinitionParser("handler-chain", new HandlerParser());
|
||||
registerBeanDefinitionParser("router", new RouterParser());
|
||||
registerBeanDefinitionParser("splitter", new SplitterParser());
|
||||
registerBeanDefinitionParser("aggregator", new AggregatorParser());
|
||||
Map<String, Class<? extends BeanDefinitionParser>> parserMappings = this.loadAdapterParserMappings();
|
||||
try {
|
||||
for (Map.Entry<String, Class<? extends BeanDefinitionParser>> entry : parserMappings.entrySet()) {
|
||||
registerBeanDefinitionParser(entry.getKey(), (BeanDefinitionParser) entry.getValue().newInstance());
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException("Failed to instantiate BeanDefinitionParser.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Class<? extends BeanDefinitionParser>> loadAdapterParserMappings() {
|
||||
Map<String, Class<? extends BeanDefinitionParser>> parserMappings =
|
||||
new HashMap<String, Class<? extends BeanDefinitionParser>>();
|
||||
ClassLoader classLoader = getClass().getClassLoader();
|
||||
try {
|
||||
Properties mappings =
|
||||
PropertiesLoaderUtils.loadAllProperties(ADAPTER_PARSER_MAPPINGS_LOCATION, classLoader);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Loaded parser mappings [" + mappings + "]");
|
||||
}
|
||||
Enumeration<?> propertyNames = mappings.propertyNames();
|
||||
while (propertyNames.hasMoreElements()) {
|
||||
String name = (String) propertyNames.nextElement();
|
||||
String classname = mappings.getProperty(name);
|
||||
Class<?> parserClass = ClassUtils.forName(classname, classLoader);
|
||||
if (!BeanDefinitionParser.class.isAssignableFrom(parserClass)) {
|
||||
throw new IllegalStateException("Expected class of type BeanDefinitionParser, but '" +
|
||||
name + "' was of type '" + parserClass.getSimpleName() + "'");
|
||||
}
|
||||
parserMappings.put(name, (Class<? extends BeanDefinitionParser>) parserClass);
|
||||
}
|
||||
return parserMappings;
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new IllegalStateException(
|
||||
"Unable to load BeanDefinitionParser mappings from location [" +
|
||||
ADAPTER_PARSER_MAPPINGS_LOCATION + "]. Root cause: " + e);
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
throw new IllegalStateException("Failed to load BeanDefinitionParser.", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.integration.endpoint.ConcurrencyPolicy;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Shared utility methods for integration namespace parsers.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
public abstract class IntegrationNamespaceUtils {
|
||||
|
||||
private static final String CORE_SIZE_ATTRIBUTE = "core";
|
||||
|
||||
private static final String MAX_SIZE_ATTRIBUTE = "max";
|
||||
|
||||
private static final String QUEUE_CAPACITY_ATTRIBUTE = "queue-capacity";
|
||||
|
||||
private static final String KEEP_ALIVE_ATTRIBUTE = "keep-alive";
|
||||
|
||||
|
||||
public static ConcurrencyPolicy parseConcurrencyPolicy(Element element) {
|
||||
ConcurrencyPolicy policy = new ConcurrencyPolicy();
|
||||
String coreSize = element.getAttribute(CORE_SIZE_ATTRIBUTE);
|
||||
String maxSize = element.getAttribute(MAX_SIZE_ATTRIBUTE);
|
||||
String queueCapacity = element.getAttribute(QUEUE_CAPACITY_ATTRIBUTE);
|
||||
String keepAlive = element.getAttribute(KEEP_ALIVE_ATTRIBUTE);
|
||||
if (StringUtils.hasText(coreSize)) {
|
||||
policy.setCoreSize(Integer.parseInt(coreSize));
|
||||
}
|
||||
if (StringUtils.hasText(maxSize)) {
|
||||
policy.setMaxSize(Integer.parseInt(maxSize));
|
||||
}
|
||||
if (StringUtils.hasText(queueCapacity)) {
|
||||
policy.setQueueCapacity(Integer.parseInt(queueCapacity));
|
||||
}
|
||||
if (StringUtils.hasText(keepAlive)) {
|
||||
policy.setKeepAliveSeconds(Integer.parseInt(keepAlive));
|
||||
}
|
||||
return policy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the property identified by propertyName on the bean definition
|
||||
* to the value of the attribute specified by attributeName, if that
|
||||
* attribute is defined in the element
|
||||
*
|
||||
* @param beanDefinition - the bean definition to be configured
|
||||
* @param propertyName - the name of the bean property to be set
|
||||
* @param element - the XML element where the attribute should be defined
|
||||
* @param attributeName - the name of the attribute whose value will be set
|
||||
* on the property
|
||||
*/
|
||||
public static void setValueIfAttributeDefined(RootBeanDefinition beanDefinition, String propertyName,
|
||||
Element element, String attributeName) {
|
||||
final String attributeValue = element.getAttribute(attributeName);
|
||||
if (StringUtils.hasText(attributeValue)) {
|
||||
beanDefinition.getPropertyValues().addPropertyValue(propertyName, attributeValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the property given by propertyName on the given bean definition
|
||||
* to a reference to a bean identified by the value of the attribute
|
||||
* specified by attributeName, if that attribute is defined in the element
|
||||
*
|
||||
* @param beanDefinition - the bean definition to be configured
|
||||
* @param propertyName - the name of the bean property to be set
|
||||
* @param element - the XML element where the attribute should be defined
|
||||
* @param attributeName - the id of the bean which will be used to populate
|
||||
* the property
|
||||
*/
|
||||
public static void setBeanReferenceIfAttributeDefined(RootBeanDefinition beanDefinition, String propertyName,
|
||||
Element element, String attributeName) {
|
||||
final String attributeValue = element.getAttribute(attributeName);
|
||||
if (StringUtils.hasText(attributeValue)) {
|
||||
beanDefinition.getPropertyValues().addPropertyValue(propertyName, new RuntimeBeanReference(attributeValue));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.core.Conventions;
|
||||
import org.springframework.integration.ConfigurationException;
|
||||
import org.springframework.integration.bus.MessageBus;
|
||||
import org.springframework.integration.bus.MessageBusAwareBeanPostProcessor;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Parser for the <em>message-bus</em> element of the integration namespace.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
public class MessageBusParser extends AbstractSimpleBeanDefinitionParser {
|
||||
|
||||
public static final String MESSAGE_BUS_BEAN_NAME = "internal.MessageBus";
|
||||
|
||||
public static final String MESSAGE_BUS_AWARE_POST_PROCESSOR_BEAN_NAME = "internal.MessageBusAwareBeanPostProcessor";
|
||||
|
||||
private static final Class<?> MESSAGE_BUS_CLASS = MessageBus.class;
|
||||
|
||||
private static final String ERROR_CHANNEL_ATTRIBUTE = "error-channel";
|
||||
|
||||
private static final String DEFAULT_CONCURRENCY_ELEMENT = "default-concurrency";
|
||||
|
||||
private static final String DEFAULT_CONCURRENCY_PROPERTY = "defaultConcurrencyPolicy";
|
||||
|
||||
private static final String CHANNEL_FACTORY_ATTRIBUTE = "channel-factory";
|
||||
|
||||
|
||||
@Override
|
||||
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
|
||||
throws BeanDefinitionStoreException {
|
||||
if (parserContext.getRegistry().containsBeanDefinition(MESSAGE_BUS_BEAN_NAME)) {
|
||||
throw new ConfigurationException("Only one instance of '" + MESSAGE_BUS_CLASS.getSimpleName()
|
||||
+ "' is allowed per ApplicationContext.");
|
||||
}
|
||||
return MESSAGE_BUS_BEAN_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> getBeanClass(Element element) {
|
||||
return MESSAGE_BUS_CLASS;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEligibleAttribute(String attributeName) {
|
||||
return !ERROR_CHANNEL_ATTRIBUTE.equals(attributeName) &&
|
||||
!CHANNEL_FACTORY_ATTRIBUTE.equals(attributeName) &&
|
||||
super.isEligibleAttribute(attributeName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postProcess(BeanDefinitionBuilder beanDefinition, Element element) {
|
||||
String errorChannelRef = element.getAttribute(ERROR_CHANNEL_ATTRIBUTE);
|
||||
if (StringUtils.hasText(errorChannelRef)) {
|
||||
beanDefinition.addPropertyReference(Conventions.attributeNameToPropertyName(
|
||||
ERROR_CHANNEL_ATTRIBUTE), errorChannelRef);
|
||||
}
|
||||
String channelFactoryRef = element.getAttribute(CHANNEL_FACTORY_ATTRIBUTE);
|
||||
if (StringUtils.hasText(channelFactoryRef)) {
|
||||
beanDefinition.addPropertyReference(Conventions.attributeNameToPropertyName(
|
||||
CHANNEL_FACTORY_ATTRIBUTE), channelFactoryRef);
|
||||
}
|
||||
this.processChildElements(beanDefinition, element);
|
||||
}
|
||||
|
||||
private void processChildElements(BeanDefinitionBuilder beanDefinition, Element element) {
|
||||
NodeList childNodes = element.getChildNodes();
|
||||
for (int i = 0; i < childNodes.getLength(); i++) {
|
||||
Node child = childNodes.item(i);
|
||||
if (child.getNodeType() == Node.ELEMENT_NODE) {
|
||||
String localName = child.getLocalName();
|
||||
if (DEFAULT_CONCURRENCY_ELEMENT.equals(localName)) {
|
||||
beanDefinition.addPropertyValue(DEFAULT_CONCURRENCY_PROPERTY,
|
||||
IntegrationNamespaceUtils.parseConcurrencyPolicy((Element) child));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||
super.doParse(element, parserContext, builder);
|
||||
this.addPostProcessors(parserContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds extra post-processors to the context, to inject the objects configured by the MessageBus
|
||||
*/
|
||||
private void addPostProcessors(ParserContext parserContext) {
|
||||
BeanDefinition postProcessorDefinition = new RootBeanDefinition(MessageBusAwareBeanPostProcessor.class);
|
||||
postProcessorDefinition.getConstructorArgumentValues().addGenericArgumentValue(
|
||||
new RuntimeBeanReference(MessageBusParser.MESSAGE_BUS_BEAN_NAME));
|
||||
parserContext.getRegistry().registerBeanDefinition(MESSAGE_BUS_AWARE_POST_PROCESSOR_BEAN_NAME,
|
||||
postProcessorDefinition);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.config;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.core.OrderComparator;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.integration.ConfigurationException;
|
||||
import org.springframework.integration.annotation.Aggregator;
|
||||
import org.springframework.integration.annotation.CompletionStrategy;
|
||||
import org.springframework.integration.annotation.Concurrency;
|
||||
import org.springframework.integration.annotation.DefaultOutput;
|
||||
import org.springframework.integration.annotation.Handler;
|
||||
import org.springframework.integration.annotation.MessageEndpoint;
|
||||
import org.springframework.integration.annotation.Polled;
|
||||
import org.springframework.integration.annotation.Router;
|
||||
import org.springframework.integration.annotation.Splitter;
|
||||
import org.springframework.integration.bus.MessageBus;
|
||||
import org.springframework.integration.channel.ChannelRegistryAware;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.endpoint.ConcurrencyPolicy;
|
||||
import org.springframework.integration.endpoint.HandlerEndpoint;
|
||||
import org.springframework.integration.endpoint.SourceEndpoint;
|
||||
import org.springframework.integration.handler.AbstractMessageHandlerAdapter;
|
||||
import org.springframework.integration.handler.MessageHandler;
|
||||
import org.springframework.integration.handler.MessageHandlerChain;
|
||||
import org.springframework.integration.handler.MethodInvokingTarget;
|
||||
import org.springframework.integration.handler.config.DefaultMessageHandlerCreator;
|
||||
import org.springframework.integration.handler.config.MessageHandlerCreator;
|
||||
import org.springframework.integration.message.MethodInvokingSource;
|
||||
import org.springframework.integration.router.AggregatingMessageHandler;
|
||||
import org.springframework.integration.router.CompletionStrategyAdapter;
|
||||
import org.springframework.integration.router.config.AggregatorMessageHandlerCreator;
|
||||
import org.springframework.integration.router.config.RouterMessageHandlerCreator;
|
||||
import org.springframework.integration.router.config.SplitterMessageHandlerCreator;
|
||||
import org.springframework.integration.scheduling.PollingSchedule;
|
||||
import org.springframework.integration.scheduling.Subscription;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A {@link BeanPostProcessor} implementation that generates endpoints for
|
||||
* classes annotated with {@link MessageEndpoint @MessageEndpoint}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
public class MessageEndpointAnnotationPostProcessor implements BeanPostProcessor, InitializingBean {
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
private final Map<Class<? extends Annotation>, MessageHandlerCreator> handlerCreators = new ConcurrentHashMap<Class<? extends Annotation>, MessageHandlerCreator>();
|
||||
|
||||
private final MessageBus messageBus;
|
||||
|
||||
|
||||
public MessageEndpointAnnotationPostProcessor(MessageBus messageBus) {
|
||||
Assert.notNull(messageBus, "'messageBus' must not be null");
|
||||
this.messageBus = messageBus;
|
||||
}
|
||||
|
||||
|
||||
public void setCustomHandlerCreators(Map<Class<? extends Annotation>, MessageHandlerCreator> customHandlerCreators) {
|
||||
for (Map.Entry<Class<? extends Annotation>, MessageHandlerCreator> entry : customHandlerCreators.entrySet()) {
|
||||
this.handlerCreators.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
this.handlerCreators.put(Handler.class, new DefaultMessageHandlerCreator());
|
||||
this.handlerCreators.put(Router.class, new RouterMessageHandlerCreator());
|
||||
this.handlerCreators.put(Splitter.class, new SplitterMessageHandlerCreator());
|
||||
this.handlerCreators.put(Aggregator.class, new AggregatorMessageHandlerCreator(messageBus));
|
||||
}
|
||||
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
Class<?> beanClass = this.getBeanClass(bean);
|
||||
MessageEndpoint endpointAnnotation = AnnotationUtils.findAnnotation(beanClass, MessageEndpoint.class);
|
||||
if (endpointAnnotation == null) {
|
||||
return bean;
|
||||
}
|
||||
if (bean instanceof ChannelRegistryAware) {
|
||||
((ChannelRegistryAware) bean).setChannelRegistry(this.messageBus);
|
||||
}
|
||||
String outputChannelName = endpointAnnotation.output();
|
||||
MessageHandlerChain handlerChain = this.createHandlerChain(bean, outputChannelName);
|
||||
if (handlerChain == null) {
|
||||
throw new ConfigurationException("@MessageEndpoint has no handler method");
|
||||
}
|
||||
HandlerEndpoint endpoint = new HandlerEndpoint(handlerChain);
|
||||
Polled polledAnnotation = AnnotationUtils.findAnnotation(beanClass, Polled.class);
|
||||
this.configureInput(bean, beanName, endpointAnnotation, polledAnnotation, endpoint);
|
||||
if (StringUtils.hasText(outputChannelName)) {
|
||||
endpoint.setOutputChannelName(outputChannelName);
|
||||
}
|
||||
else {
|
||||
this.configureOutput(bean, beanName, endpoint);
|
||||
}
|
||||
Concurrency concurrencyAnnotation = AnnotationUtils.findAnnotation(beanClass, Concurrency.class);
|
||||
if (concurrencyAnnotation != null) {
|
||||
ConcurrencyPolicy concurrencyPolicy = new ConcurrencyPolicy(concurrencyAnnotation.coreSize(),
|
||||
concurrencyAnnotation.maxSize());
|
||||
concurrencyPolicy.setKeepAliveSeconds(concurrencyAnnotation.keepAliveSeconds());
|
||||
concurrencyPolicy.setQueueCapacity(concurrencyAnnotation.queueCapacity());
|
||||
endpoint.setConcurrencyPolicy(concurrencyPolicy);
|
||||
}
|
||||
this.configureCompletionStrategy(bean, endpoint);
|
||||
this.messageBus.registerEndpoint(beanName + "-endpoint", endpoint);
|
||||
return bean;
|
||||
}
|
||||
|
||||
private void configureInput(final Object bean, final String beanName, MessageEndpoint annotation,
|
||||
Polled polledAnnotation, final HandlerEndpoint endpoint) {
|
||||
String channelName = annotation.input();
|
||||
if (StringUtils.hasText(channelName)) {
|
||||
PollingSchedule schedule = null;
|
||||
if (polledAnnotation != null) {
|
||||
schedule = new PollingSchedule(polledAnnotation.period());
|
||||
schedule.setInitialDelay(polledAnnotation.initialDelay());
|
||||
schedule.setFixedRate(polledAnnotation.fixedRate());
|
||||
schedule.setTimeUnit(polledAnnotation.timeUnit());
|
||||
}
|
||||
Subscription subscription = new Subscription(channelName, schedule);
|
||||
endpoint.setSubscription(subscription);
|
||||
}
|
||||
ReflectionUtils.doWithMethods(this.getBeanClass(bean), new ReflectionUtils.MethodCallback() {
|
||||
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
|
||||
Annotation annotation = AnnotationUtils.getAnnotation(method, Polled.class);
|
||||
if (annotation != null) {
|
||||
Polled polledAnnotation = (Polled) annotation;
|
||||
int period = polledAnnotation.period();
|
||||
long initialDelay = polledAnnotation.initialDelay();
|
||||
boolean fixedRate = polledAnnotation.fixedRate();
|
||||
MethodInvokingSource source = new MethodInvokingSource();
|
||||
source.setObject(bean);
|
||||
source.setMethod(method.getName());
|
||||
DirectChannel channel = new DirectChannel();
|
||||
PollingSchedule schedule = new PollingSchedule(period);
|
||||
schedule.setInitialDelay(initialDelay);
|
||||
schedule.setFixedRate(fixedRate);
|
||||
SourceEndpoint sourceEndpoint = new SourceEndpoint(source, channel, schedule);
|
||||
String channelName = beanName + "-inputChannel";
|
||||
messageBus.registerChannel(channelName, channel);
|
||||
messageBus.registerEndpoint(beanName + "-sourceEndpoint", sourceEndpoint);
|
||||
Subscription subscription = new Subscription(channel);
|
||||
endpoint.setSubscription(subscription);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void configureOutput(final Object bean, final String beanName, final HandlerEndpoint endpoint) {
|
||||
ReflectionUtils.doWithMethods(this.getBeanClass(bean), new ReflectionUtils.MethodCallback() {
|
||||
boolean foundOutput = false;
|
||||
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
|
||||
Annotation annotation = AnnotationUtils.getAnnotation(method, DefaultOutput.class);
|
||||
if (annotation != null) {
|
||||
if (foundOutput) {
|
||||
throw new ConfigurationException("only one @DefaultOutput allowed per endpoint");
|
||||
}
|
||||
MethodInvokingTarget target = new MethodInvokingTarget();
|
||||
target.setObject(bean);
|
||||
target.setMethodName(method.getName());
|
||||
target.afterPropertiesSet();
|
||||
MessageHandler handler = endpoint.getHandler();
|
||||
((MessageHandlerChain) handler).add(target);
|
||||
foundOutput = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void configureCompletionStrategy(final Object bean, final HandlerEndpoint endpoint) {
|
||||
ReflectionUtils.doWithMethods(bean.getClass(), new ReflectionUtils.MethodCallback() {
|
||||
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
|
||||
Annotation annotation = AnnotationUtils.getAnnotation(method, CompletionStrategy.class);
|
||||
if (annotation != null) {
|
||||
final MessageHandler endpointHandler = endpoint.getHandler();
|
||||
AggregatingMessageHandler aggregatingMessageHandler = null;
|
||||
if (endpointHandler != null) {
|
||||
if (endpointHandler instanceof MessageHandlerChain) {
|
||||
for (MessageHandler handlerInChain : ((MessageHandlerChain) endpointHandler).getHandlers()) {
|
||||
if (handlerInChain instanceof AggregatingMessageHandler) {
|
||||
aggregatingMessageHandler = (AggregatingMessageHandler) handlerInChain;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (endpointHandler instanceof AggregatingMessageHandler) {
|
||||
aggregatingMessageHandler = (AggregatingMessageHandler) endpointHandler;
|
||||
}
|
||||
}
|
||||
if (aggregatingMessageHandler == null) {
|
||||
throw new ConfigurationException(
|
||||
"@CompletionStrategy supported only when @Aggregator is present");
|
||||
}
|
||||
else {
|
||||
aggregatingMessageHandler.setCompletionStrategy(new CompletionStrategyAdapter(bean, method));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private MessageHandlerChain createHandlerChain(final Object bean, final String outputChannelName) {
|
||||
final List<MessageHandler> handlers = new ArrayList<MessageHandler>();
|
||||
ReflectionUtils.doWithMethods(this.getBeanClass(bean), new ReflectionUtils.MethodCallback() {
|
||||
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
|
||||
Annotation[] annotations = AnnotationUtils.getAnnotations(method);
|
||||
for (Annotation annotation : annotations) {
|
||||
if (isHandlerAnnotation(annotation)) {
|
||||
Map<String, Object> attributes = AnnotationUtils.getAnnotationAttributes(annotation);
|
||||
attributes.put(AbstractMessageHandlerAdapter.OUTPUT_CHANNEL_NAME_KEY, outputChannelName);
|
||||
MessageHandlerCreator handlerCreator = handlerCreators.get(annotation.annotationType());
|
||||
if (handlerCreator == null) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("No handler creator has been registered for handler annotation '"
|
||||
+ annotation.annotationType() + "'");
|
||||
}
|
||||
}
|
||||
else {
|
||||
MessageHandler handler = handlerCreator.createHandler(bean, method, attributes);
|
||||
if (handler instanceof ChannelRegistryAware) {
|
||||
((ChannelRegistryAware) handler).setChannelRegistry(messageBus);
|
||||
}
|
||||
if (handler instanceof InitializingBean) {
|
||||
try {
|
||||
((InitializingBean) handler).afterPropertiesSet();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new ConfigurationException("failed to create handler", e);
|
||||
}
|
||||
}
|
||||
if (handler != null) {
|
||||
handlers.add(handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
if (handlers.size() > 0) {
|
||||
MessageHandlerChain handlerChain = new MessageHandlerChain();
|
||||
Collections.sort(handlers, new OrderComparator());
|
||||
for (MessageHandler handler : handlers) {
|
||||
handlerChain.add(handler);
|
||||
}
|
||||
return handlerChain;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Class<?> getBeanClass(Object bean) {
|
||||
return AopUtils.getTargetClass(bean);
|
||||
}
|
||||
|
||||
private boolean isHandlerAnnotation(Annotation annotation) {
|
||||
return annotation.annotationType().equals(Handler.class)
|
||||
|| annotation.annotationType().isAnnotationPresent(Handler.class)
|
||||
|| this.handlerCreators.keySet().contains(annotation.annotationType());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
|
||||
import org.springframework.integration.ConfigurationException;
|
||||
import org.springframework.integration.handler.MethodInvokingTarget;
|
||||
import org.springframework.integration.message.MethodInvokingSource;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Parser for <source-adapter> and <target-adapter>.
|
||||
* Creates a {@link MethodInvokingSource} or {@link MethodInvokingTarget}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class MethodInvokingAdapterParser extends AbstractSingleBeanDefinitionParser {
|
||||
|
||||
@Override
|
||||
protected Class<?> getBeanClass(Element element) {
|
||||
return "source-adapter".equals(element.getLocalName()) ? MethodInvokingSource.class : MethodInvokingTarget.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doParse(Element element, BeanDefinitionBuilder builder) {
|
||||
String ref = element.getAttribute("ref");
|
||||
String method = element.getAttribute("method");
|
||||
if (!StringUtils.hasText(ref)) {
|
||||
throw new ConfigurationException("The 'ref' attribute is required.");
|
||||
}
|
||||
if (!StringUtils.hasText(method)) {
|
||||
throw new ConfigurationException("The 'method' attribute is required.");
|
||||
}
|
||||
builder.addPropertyReference("object", ref);
|
||||
builder.addPropertyValue("method", method);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.config;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.integration.annotation.Publisher;
|
||||
import org.springframework.integration.aop.PublisherAnnotationAdvisor;
|
||||
import org.springframework.integration.channel.ChannelRegistry;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link BeanPostProcessor} that adds a message publishing interceptor when
|
||||
* it discovers annotated methods.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class PublisherAnnotationPostProcessor implements BeanPostProcessor, BeanClassLoaderAware {
|
||||
|
||||
private Class<? extends Annotation> publisherAnnotationType = Publisher.class;
|
||||
|
||||
private String channelNameAttribute = "channel";
|
||||
|
||||
private ChannelRegistry channelRegistry;
|
||||
|
||||
private Advisor advisor;
|
||||
|
||||
private ClassLoader beanClassLoader;
|
||||
|
||||
|
||||
public void setBeanClassLoader(ClassLoader beanClassLoader) {
|
||||
Assert.notNull(beanClassLoader, "'beanClassLoader' must not be null");
|
||||
this.beanClassLoader = beanClassLoader;
|
||||
}
|
||||
|
||||
public void setPublisherAnnotationType(Class<? extends Annotation> publisherAnnotationType) {
|
||||
Assert.notNull(publisherAnnotationType, "'publisherAnnotationType' must not be null");
|
||||
this.publisherAnnotationType = publisherAnnotationType;
|
||||
}
|
||||
|
||||
public void setChannelNameAttribute(String channelNameAttribute) {
|
||||
Assert.notNull(channelNameAttribute, "'channelNameAttribute' must not be null");
|
||||
this.channelNameAttribute = channelNameAttribute;
|
||||
}
|
||||
|
||||
public void setChannelRegistry(ChannelRegistry channelRegistry) {
|
||||
Assert.notNull(channelRegistry, "'channelRegistry' must not be null");
|
||||
this.channelRegistry = channelRegistry;
|
||||
}
|
||||
|
||||
private void createAdvisor() {
|
||||
if (this.channelRegistry == null) {
|
||||
throw new IllegalStateException("'channelRegistry' is required");
|
||||
}
|
||||
this.advisor = new PublisherAnnotationAdvisor(this.publisherAnnotationType, this.channelNameAttribute,
|
||||
this.channelRegistry);
|
||||
}
|
||||
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
Class<?> targetClass = AopUtils.getTargetClass(bean);
|
||||
if (targetClass == null) {
|
||||
return bean;
|
||||
}
|
||||
if (advisor == null) {
|
||||
createAdvisor();
|
||||
}
|
||||
if (AopUtils.canApply(this.advisor, targetClass)) {
|
||||
if (bean instanceof Advised) {
|
||||
((Advised) bean).addAdvisor(this.advisor);
|
||||
return bean;
|
||||
}
|
||||
else {
|
||||
ProxyFactory pf = new ProxyFactory(bean);
|
||||
pf.addAdvisor(this.advisor);
|
||||
return pf.getProxy(this.beanClassLoader);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.ConfigurationException;
|
||||
import org.springframework.integration.endpoint.SourceEndpoint;
|
||||
import org.springframework.integration.scheduling.PollingSchedule;
|
||||
import org.springframework.integration.scheduling.Schedule;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
|
||||
/**
|
||||
* Parser for the <source-endpoint/> element.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class SourceEndpointParser extends AbstractSimpleBeanDefinitionParser {
|
||||
|
||||
protected final Class<?> getBeanClass(Element element) {
|
||||
return SourceEndpoint.class;
|
||||
}
|
||||
|
||||
protected boolean shouldGenerateId() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean shouldGenerateIdAsFallback() {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean isEligibleAttribute(String name) {
|
||||
return (!"source".equals(name) && !"channel".equals(name) && super.isEligibleAttribute(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||
String source = element.getAttribute("source");
|
||||
if (!StringUtils.hasText(source)) {
|
||||
throw new ConfigurationException("'source' is required");
|
||||
}
|
||||
String output = element.getAttribute("channel");
|
||||
if (!StringUtils.hasText(output)) {
|
||||
throw new ConfigurationException("'channel' is required");
|
||||
}
|
||||
builder.addConstructorArgReference(source);
|
||||
builder.addConstructorArgReference(output);
|
||||
Element scheduleElement = this.getScheduleElement(element);
|
||||
if (scheduleElement == null) {
|
||||
throw new ConfigurationException("The <schedule/> sub-element is required for a <source-endpoint/>.");
|
||||
}
|
||||
builder.addConstructorArgValue(this.parseSchedule(scheduleElement));
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses may override this method to control the creation of the {@link Schedule}. The default
|
||||
* implementation creates a {@link PollingSchedule} instance based on the provided "period" attribute.
|
||||
*/
|
||||
protected Schedule parseSchedule(Element element) {
|
||||
String period = element.getAttribute("period");
|
||||
if (!StringUtils.hasText(period)) {
|
||||
throw new ConfigurationException("The 'period' attribute is required for the 'schedule' element.");
|
||||
}
|
||||
PollingSchedule schedule = new PollingSchedule(Long.valueOf(period));
|
||||
return schedule;
|
||||
}
|
||||
|
||||
private Element getScheduleElement(Element element) {
|
||||
return DomUtils.getChildElementByTagName(element, "schedule");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.config;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.integration.annotation.Subscriber;
|
||||
import org.springframework.integration.bus.MessageBus;
|
||||
import org.springframework.integration.handler.DefaultMessageHandlerAdapter;
|
||||
import org.springframework.integration.scheduling.Subscription;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* A {@link BeanPostProcessor} that creates a method-invoking handler adapter
|
||||
* when it discovers methods annotated with {@link Subscriber @Subscriber}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class SubscriberAnnotationPostProcessor implements BeanPostProcessor {
|
||||
|
||||
private Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
private Class<? extends Annotation> subscriberAnnotationType = Subscriber.class;
|
||||
|
||||
private String channelNameAttribute = "channel";
|
||||
|
||||
private MessageBus messageBus;
|
||||
|
||||
|
||||
public void setSubscriberAnnotationType(Class<? extends Annotation> subscriberAnnotationType) {
|
||||
Assert.notNull(subscriberAnnotationType, "'subscriberAnnotationType' must not be null");
|
||||
this.subscriberAnnotationType = subscriberAnnotationType;
|
||||
}
|
||||
|
||||
public void setChannelNameAttribute(String channelNameAttribute) {
|
||||
Assert.notNull(channelNameAttribute, "'channelNameAttribute' must not be null");
|
||||
this.channelNameAttribute = channelNameAttribute;
|
||||
}
|
||||
|
||||
public void setMessageBus(MessageBus messageBus) {
|
||||
Assert.notNull(messageBus, "'messageBus' must not be null");
|
||||
this.messageBus = messageBus;
|
||||
}
|
||||
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
|
||||
final Class<?> targetClass = AopUtils.getTargetClass(bean);
|
||||
if (targetClass == null) {
|
||||
return bean;
|
||||
}
|
||||
if (this.messageBus == null) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn(this.getClass().getSimpleName() + " is disabled since no 'messageBus' was provided");
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
ReflectionUtils.doWithMethods(targetClass, new ReflectionUtils.MethodCallback() {
|
||||
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
|
||||
Annotation annotation = method.getAnnotation(subscriberAnnotationType);
|
||||
if (annotation != null) {
|
||||
String channelName = (String) AnnotationUtils.getValue(annotation, channelNameAttribute);
|
||||
DefaultMessageHandlerAdapter adapter = new DefaultMessageHandlerAdapter();
|
||||
adapter.setMethodName(method.getName());
|
||||
adapter.setObject(bean);
|
||||
adapter.afterPropertiesSet();
|
||||
String adapterName = ClassUtils.getShortNameAsProperty(targetClass) +
|
||||
"-" + method.getName() + "-endpoint";
|
||||
Subscription subscription = new Subscription(channelName);
|
||||
messageBus.registerHandler(adapterName, adapter, subscription);
|
||||
}
|
||||
}
|
||||
});
|
||||
return bean;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.integration.endpoint.TargetEndpoint;
|
||||
import org.springframework.integration.handler.MethodInvokingTarget;
|
||||
|
||||
/**
|
||||
* Parser for the <target-endpoint> element.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class TargetEndpointParser extends AbstractTargetEndpointParser {
|
||||
|
||||
@Override
|
||||
protected Class<?> getBeanClass(Element element) {
|
||||
return TargetEndpoint.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTargetAttributeName() {
|
||||
return "target";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> getAdapterClass() {
|
||||
return MethodInvokingTarget.class;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,385 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<xsd:schema xmlns="http://www.springframework.org/schema/integration"
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:tool="http://www.springframework.org/schema/tool"
|
||||
targetNamespace="http://www.springframework.org/schema/integration"
|
||||
elementFormDefault="qualified"
|
||||
attributeFormDefault="unqualified">
|
||||
|
||||
<xsd:import namespace="http://www.springframework.org/schema/beans"/>
|
||||
<xsd:import namespace="http://www.springframework.org/schema/tool"/>
|
||||
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Defines the core configuration elements for Spring Integration.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
|
||||
<xsd:element name="message-bus">
|
||||
<xsd:complexType>
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Defines a message bus.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="default-concurrency" type="concurrencyType" minOccurs="0" maxOccurs="1"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="auto-startup" type="xsd:boolean"/>
|
||||
<xsd:attribute name="auto-create-channels" type="xsd:boolean"/>
|
||||
<xsd:attribute name="channel-factory" type="xsd:string"/>
|
||||
<xsd:attribute name="error-channel" type="xsd:string"/>
|
||||
<xsd:attribute name="dispatcher-pool-size" type="xsd:int"/>
|
||||
<xsd:attribute name="configure-async-event-multicaster" type="xsd:boolean"/>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="annotation-driven">
|
||||
<xsd:complexType>
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Enables the annotation-driven post-processors.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="channel" type="channelType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Defines a generic channel type. The actual channel type
|
||||
will be determined by the channel factory set on the message bus.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="queue-channel" type="capacityChannelType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Defines a channel that buffers messages in a queue.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="direct-channel">
|
||||
<xsd:complexType>
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Defines a channel that invokes its handlers directly in the sender's thread.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="channelType">
|
||||
<xsd:attribute name="source" type="xsd:string"/>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="priority-channel">
|
||||
<xsd:complexType>
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Defines a channel with priority-ordering for message reception.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="capacityChannelType">
|
||||
<xsd:attribute name="comparator" type="xsd:string"/>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="rendezvous-channel" type="channelType"/>
|
||||
|
||||
<xsd:element name="thread-local-channel" type="channelType"/>
|
||||
|
||||
<xsd:complexType name="capacityChannelType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Defines a channel with a configurable capacity.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="channelType">
|
||||
<xsd:attribute name="capacity" type="xsd:integer"/>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="channelType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Defines a message channel.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:sequence>
|
||||
<xsd:element ref="dispatcher-policy" minOccurs="0" maxOccurs="1"/>
|
||||
<xsd:element ref="interceptor" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="id" type="xsd:ID" use="required"/>
|
||||
<xsd:attribute name="publish-subscribe" type="xsd:boolean" default="false"/>
|
||||
<xsd:attribute name="datatype" type="xsd:string"/>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:element name="interceptor">
|
||||
<xsd:complexType>
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Provides a channel interceptor reference.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:attribute name="ref" type="xsd:string" use="required"/>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="source-endpoint">
|
||||
<xsd:complexType>
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Defines a source endpoint.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="beans:identifiedType">
|
||||
<xsd:sequence>
|
||||
<xsd:element ref="schedule" minOccurs="1" maxOccurs="1"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="source" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="channel" type="xsd:string" use="required"/>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="source-adapter" type="methodInvokingAdapterType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Defines a MethodInvokingSource.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="target-endpoint">
|
||||
<xsd:complexType>
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Defines a target endpoint.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="targetEndpointType">
|
||||
<xsd:attribute name="target" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="method" type="xsd:string"/>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="target-adapter" type="methodInvokingAdapterType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Defines a MethodInvokingTarget.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="handler-endpoint">
|
||||
<xsd:complexType>
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Defines an endpoint for a MessageHandler.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="targetEndpointType">
|
||||
<xsd:attribute name="handler" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="method" type="xsd:string"/>
|
||||
<xsd:attribute name="output-channel" type="xsd:string"/>
|
||||
<xsd:attribute name="reply-handler" type="xsd:string"/>
|
||||
<xsd:attribute name="return-address-overrides" type="xsd:boolean" default="false"/>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="schedule">
|
||||
<xsd:complexType>
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Defines a schedule.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:attribute name="period" type="xsd:int"/>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="selector">
|
||||
<xsd:complexType>
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Provides a message selector reference.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:attribute name="ref" type="xsd:string" use="required"/>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="handler-chain">
|
||||
<xsd:complexType>
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Defines a MessageHandler chain.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:sequence>
|
||||
<xsd:element ref="handler" minOccurs="1" maxOccurs="unbounded"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="id" type="xsd:ID"/>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="handler">
|
||||
<xsd:complexType>
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Defines a handler.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:attribute name="id" type="xsd:ID"/>
|
||||
<xsd:attribute name="ref" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="method" type="xsd:string"/>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="dispatcher-policy">
|
||||
<xsd:complexType>
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Defines a dispatcher policy.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:attribute name="max-messages-per-task" type="xsd:int"/>
|
||||
<xsd:attribute name="receive-timeout" type="xsd:long"/>
|
||||
<xsd:attribute name="rejection-limit" type="xsd:int"/>
|
||||
<xsd:attribute name="retry-interval" type="xsd:long"/>
|
||||
<xsd:attribute name="should-fail-on-rejection-limit" type="xsd:boolean"/>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:complexType name="concurrencyType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Defines a concurrency policy.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:attribute name="core" type="xsd:int"/>
|
||||
<xsd:attribute name="max" type="xsd:int"/>
|
||||
<xsd:attribute name="queue-capacity" type="xsd:int"/>
|
||||
<xsd:attribute name="keep-alive" type="xsd:int"/>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:element name="router">
|
||||
<xsd:complexType>
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Defines a Router.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:attribute name="id" type="xsd:ID"/>
|
||||
<xsd:attribute name="ref" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="method" type="xsd:string" use="required"/>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="splitter">
|
||||
<xsd:complexType>
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Defines a Splitter.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:attribute name="id" type="xsd:ID"/>
|
||||
<xsd:attribute name="ref" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="method" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="output-channel" type="xsd:string" use="required"/>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="aggregator">
|
||||
<xsd:complexType>
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Defines an aggregating message handler.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="beans:identifiedType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="completion-strategy" minOccurs="0" maxOccurs="1"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="ref" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="method" type="xsd:string" use="optional"/>
|
||||
<xsd:attribute name="completion-strategy" type="xsd:string" use="optional"/>
|
||||
<xsd:attribute name="completion-strategy-method" type="xsd:string" use="optional"/>
|
||||
<xsd:attribute name="default-reply-channel" type="xsd:string" use="optional"/>
|
||||
<xsd:attribute name="discard-channel" type="xsd:string" use="optional"/>
|
||||
<xsd:attribute name="send-timeout" type="xsd:long" use="optional"/>
|
||||
<xsd:attribute name="send-partial-result-on-timeout" type="xsd:boolean" use="optional"/>
|
||||
<xsd:attribute name="tracked-correlation-id-capacity" type="xsd:int" use="optional"/>
|
||||
<xsd:attribute name="reaper-interval" type="xsd:long" use="optional"/>
|
||||
<xsd:attribute name="timeout" type="xsd:long" use="optional"/>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="completion-strategy">
|
||||
<xsd:complexType>
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Defines a completion strategy.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:attribute name="ref" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="method" type="xsd:string" use="optional"/>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:complexType name="targetEndpointType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Defines common configuration properties of target message endpoints.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="beans:identifiedType">
|
||||
<xsd:sequence>
|
||||
<xsd:element ref="schedule" minOccurs="0" maxOccurs="1"/>
|
||||
<xsd:element name="concurrency" type="concurrencyType" minOccurs="0" maxOccurs="1"/>
|
||||
<xsd:element ref="selector" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="input-channel" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="error-handler" type="xsd:string"/>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="methodInvokingAdapterType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Base type for method-invoking adapters.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="beans:identifiedType">
|
||||
<xsd:attribute name="ref" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="method" type="xsd:string" use="required"/>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
|
||||
</xsd:schema>
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.dispatcher;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.integration.channel.DispatcherPolicy;
|
||||
import org.springframework.integration.channel.MessageChannel;
|
||||
import org.springframework.integration.message.BlockingSource;
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.message.MessageDeliveryAware;
|
||||
import org.springframework.integration.message.MessageDeliveryException;
|
||||
import org.springframework.integration.message.Source;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A subclass of {@link SimpleDispatcher} that adds message retrieval
|
||||
* capabilities by polling a {@link PollableSource} for {@link Message Messages}.
|
||||
* The number of messages retrieved per poll is limited by the '<em>maxMessagesPerTask</em>'
|
||||
* property of the provided {@link DispatcherPolicy}, and the timeout for each
|
||||
* receive call is determined by the policy's '<em>receiveTimeout</em>'
|
||||
* property. In general, it is recommended to use a value of 1 (the default) for
|
||||
* 'maxMessagesPerTask' whenever a significant timeout is provided. Otherwise
|
||||
* the poller may be holding on to available messages while waiting for
|
||||
* additional messages. Note that the 'timeout' value is only relevant if the
|
||||
* specified source is an implementation of {@link BlockingSource}. The default
|
||||
* timeout value is 0 indicating that the method should return immediately
|
||||
* rather than waiting for a {@link Message} to become available.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class DefaultPollingDispatcher extends SimpleDispatcher implements PollingDispatcher {
|
||||
|
||||
private final Source<?> source;
|
||||
|
||||
|
||||
public DefaultPollingDispatcher(MessageChannel channel) {
|
||||
this(channel, channel.getDispatcherPolicy());
|
||||
}
|
||||
|
||||
public DefaultPollingDispatcher(Source<?> source, DispatcherPolicy dispatcherPolicy) {
|
||||
super(dispatcherPolicy);
|
||||
Assert.notNull(source, "source must not be null");
|
||||
this.source = source;
|
||||
this.dispatcherPolicy.setReceiveTimeout(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatch(Message<?> message) {
|
||||
boolean sent = super.dispatch(message);
|
||||
if (this.source instanceof MessageDeliveryAware) {
|
||||
if (sent) {
|
||||
((MessageDeliveryAware) this.source).onSend(message);
|
||||
}
|
||||
else {
|
||||
((MessageDeliveryAware) this.source).onFailure(new MessageDeliveryException(message, "failed to send message"));
|
||||
}
|
||||
}
|
||||
return sent;
|
||||
}
|
||||
|
||||
public List<Message<?>> poll() {
|
||||
List<Message<?>> messages = new LinkedList<Message<?>>();
|
||||
int limit = this.dispatcherPolicy.getMaxMessagesPerTask();
|
||||
while (messages.size() < limit) {
|
||||
Message<?> message = null;
|
||||
long timeout = this.dispatcherPolicy.getReceiveTimeout();
|
||||
if (this.source instanceof BlockingSource && timeout >= 0) {
|
||||
message = ((BlockingSource<?>) this.source).receive(timeout);
|
||||
}
|
||||
else {
|
||||
message = this.source.receive();
|
||||
}
|
||||
if (message == null) {
|
||||
return messages;
|
||||
}
|
||||
messages.add(message);
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.dispatcher;
|
||||
|
||||
import org.springframework.integration.message.Message;
|
||||
|
||||
/**
|
||||
* Strategy interface for dispatching messages.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public interface MessageDispatcher {
|
||||
|
||||
boolean dispatch(Message<?> message);
|
||||
|
||||
void setSendTimeout(long timeout);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.dispatcher;
|
||||
|
||||
import org.springframework.integration.message.Poller;
|
||||
import org.springframework.integration.message.Subscribable;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public interface PollingDispatcher extends Poller, MessageDispatcher, Subscribable {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.dispatcher;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.scheduling.MessagingTask;
|
||||
import org.springframework.integration.scheduling.Schedule;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link MessagingTask} that combines polling and dispatching.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class PollingDispatcherTask implements MessagingTask {
|
||||
|
||||
private final PollingDispatcher dispatcher;
|
||||
|
||||
private final Schedule schedule;
|
||||
|
||||
|
||||
public PollingDispatcherTask(PollingDispatcher dispatcher, Schedule schedule) {
|
||||
Assert.notNull(dispatcher, "dispatcher must not be null");
|
||||
this.dispatcher = dispatcher;
|
||||
this.schedule = schedule;
|
||||
}
|
||||
|
||||
|
||||
public PollingDispatcher getDispatcher() {
|
||||
return this.dispatcher;
|
||||
}
|
||||
|
||||
public Schedule getSchedule() {
|
||||
return this.schedule;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
List<Message<?>> messages = this.dispatcher.poll();
|
||||
for (Message<?> message : messages) {
|
||||
this.dispatcher.dispatch(message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.dispatcher;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.integration.channel.DispatcherPolicy;
|
||||
import org.springframework.integration.handler.MessageHandlerNotRunningException;
|
||||
import org.springframework.integration.handler.MessageHandlerRejectedExecutionException;
|
||||
import org.springframework.integration.message.BlockingTarget;
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.message.MessageDeliveryException;
|
||||
import org.springframework.integration.message.Subscribable;
|
||||
import org.springframework.integration.message.Target;
|
||||
|
||||
/**
|
||||
* Basic implementation of {@link MessageDispatcher}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class SimpleDispatcher implements MessageDispatcher, Subscribable {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
private final List<Target> targets = new CopyOnWriteArrayList<Target>();
|
||||
|
||||
protected final DispatcherPolicy dispatcherPolicy;
|
||||
|
||||
private volatile long sendTimeout;
|
||||
|
||||
|
||||
public SimpleDispatcher(DispatcherPolicy dispatcherPolicy) {
|
||||
this.dispatcherPolicy = (dispatcherPolicy != null) ? dispatcherPolicy : new DispatcherPolicy();
|
||||
}
|
||||
|
||||
|
||||
public void setSendTimeout(long sendTimeout) {
|
||||
this.sendTimeout = sendTimeout;
|
||||
}
|
||||
|
||||
public boolean subscribe(Target target) {
|
||||
return this.targets.add(target);
|
||||
}
|
||||
|
||||
public boolean unsubscribe(Target target) {
|
||||
return this.targets.remove(target);
|
||||
}
|
||||
|
||||
public boolean dispatch(Message<?> message) {
|
||||
int attempts = 0;
|
||||
List<Target> targetList = new ArrayList<Target>(this.targets);
|
||||
while (attempts < this.dispatcherPolicy.getRejectionLimit()) {
|
||||
if (attempts > 0) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("target(s) rejected message after " + attempts +
|
||||
" attempt(s), will try again after 'retryInterval' of " +
|
||||
this.dispatcherPolicy.getRetryInterval() + " milliseconds");
|
||||
}
|
||||
try {
|
||||
Thread.sleep(this.dispatcherPolicy.getRetryInterval());
|
||||
}
|
||||
catch (InterruptedException iex) {
|
||||
Thread.currentThread().interrupt();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Iterator<Target> iter = targetList.iterator();
|
||||
if (!iter.hasNext()) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("no active targets");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
boolean rejected = false;
|
||||
while (iter.hasNext()) {
|
||||
Target target = iter.next();
|
||||
try {
|
||||
boolean sent = (target instanceof BlockingTarget && this.sendTimeout >= 0) ?
|
||||
((BlockingTarget) target).send(message, this.sendTimeout) : target.send(message);
|
||||
if (!this.dispatcherPolicy.isPublishSubscribe() && sent) {
|
||||
return true;
|
||||
}
|
||||
if (!sent && logger.isDebugEnabled()) {
|
||||
logger.debug("target rejected message, continuing with other targets if available");
|
||||
}
|
||||
iter.remove();
|
||||
}
|
||||
catch (MessageHandlerNotRunningException e) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("target is not running, continuing with other targets if available", e);
|
||||
}
|
||||
}
|
||||
catch (MessageHandlerRejectedExecutionException e) {
|
||||
rejected = true;
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("target is busy, continuing with other targets if available", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!rejected) {
|
||||
return true;
|
||||
}
|
||||
attempts++;
|
||||
}
|
||||
if (this.dispatcherPolicy.getShouldFailOnRejectionLimit()) {
|
||||
throw new MessageDeliveryException(message, "Dispatcher reached rejection limit of "
|
||||
+ this.dispatcherPolicy.getRejectionLimit()
|
||||
+ ". Consider increasing the target's concurrency and/or "
|
||||
+ "the dispatcherPolicy's 'rejectionLimit'.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.endpoint;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Base class for {@link MessageEndpoint} implementations.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public abstract class AbstractEndpoint implements MessageEndpoint {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
private volatile String name;
|
||||
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setBeanName(String beanName) {
|
||||
this.setName(beanName);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return (this.name != null) ? this.name : super.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.endpoint;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Metadata for configuring a pool of concurrent threads.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class ConcurrencyPolicy implements EndpointPolicy {
|
||||
|
||||
public static final int DEFAULT_CORE_SIZE = 1;
|
||||
|
||||
public static final int DEFAULT_MAX_SIZE = 10;
|
||||
|
||||
public static final int DEFAULT_QUEUE_CAPACITY = 0;
|
||||
|
||||
public static final int DEFAULT_KEEP_ALIVE_SECONDS = 60;
|
||||
|
||||
|
||||
private int coreSize = DEFAULT_CORE_SIZE;
|
||||
|
||||
private int maxSize = DEFAULT_MAX_SIZE;
|
||||
|
||||
private int queueCapacity = DEFAULT_QUEUE_CAPACITY;
|
||||
|
||||
private int keepAliveSeconds = DEFAULT_KEEP_ALIVE_SECONDS;
|
||||
|
||||
|
||||
public ConcurrencyPolicy() {
|
||||
}
|
||||
|
||||
public ConcurrencyPolicy(int coreSize, int maxSize) {
|
||||
Assert.isTrue(maxSize >= coreSize, "'coreSize' must not exceed 'maxSize'");
|
||||
this.setCoreSize(coreSize);
|
||||
this.setMaxSize(maxSize);
|
||||
}
|
||||
|
||||
|
||||
public int getCoreSize() {
|
||||
return this.coreSize;
|
||||
}
|
||||
|
||||
public void setCoreSize(int coreSize) {
|
||||
Assert.isTrue(coreSize > 0, "'coreSize' must be at least 1");
|
||||
this.coreSize = coreSize;
|
||||
}
|
||||
|
||||
public int getMaxSize() {
|
||||
return this.maxSize;
|
||||
}
|
||||
|
||||
public void setMaxSize(int maxSize) {
|
||||
Assert.isTrue(maxSize > 0, "'maxSize' must be at least 1");
|
||||
this.maxSize = maxSize;
|
||||
}
|
||||
|
||||
public int getQueueCapacity() {
|
||||
return this.queueCapacity;
|
||||
}
|
||||
|
||||
public void setQueueCapacity(int queueCapacity) {
|
||||
Assert.isTrue(queueCapacity >= 0, "'queueCapacity' must not be negative");
|
||||
this.queueCapacity = queueCapacity;
|
||||
}
|
||||
|
||||
public int getKeepAliveSeconds() {
|
||||
return this.keepAliveSeconds;
|
||||
}
|
||||
|
||||
public void setKeepAliveSeconds(int keepAliveSeconds) {
|
||||
Assert.isTrue(keepAliveSeconds >= 0, "'keepAliveSeconds' must not be negative");
|
||||
this.keepAliveSeconds = keepAliveSeconds;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "[coreSize=" + this.coreSize + ", maxSize=" + this.maxSize +
|
||||
", queueCapacity=" + this.queueCapacity + ", keepAliveSeconds=" + this.keepAliveSeconds + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.endpoint;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.integration.handler.MessageHandler;
|
||||
import org.springframework.integration.handler.MessageHandlerNotRunningException;
|
||||
import org.springframework.integration.handler.MessageHandlerRejectedExecutionException;
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.message.MessageDeliveryException;
|
||||
import org.springframework.integration.message.Target;
|
||||
import org.springframework.integration.util.ErrorHandler;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link Target} implementation that encapsulates an Executor and delegates
|
||||
* to a wrapped target for concurrent, asynchronous message handling.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class ConcurrentTarget implements Target, DisposableBean {
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
private final Target target;
|
||||
|
||||
private final ExecutorService executor;
|
||||
|
||||
private volatile ErrorHandler errorHandler;
|
||||
|
||||
|
||||
public ConcurrentTarget(Target target, ExecutorService executor) {
|
||||
Assert.notNull(target, "'target' must not be null");
|
||||
Assert.notNull(executor, "'executor' must not be null");
|
||||
this.target = target;
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
|
||||
public void setErrorHandler(ErrorHandler errorHandler) {
|
||||
this.errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
this.executor.shutdown();
|
||||
}
|
||||
|
||||
public boolean send(Message<?> message) {
|
||||
if (this.executor.isShutdown()) {
|
||||
throw new MessageHandlerNotRunningException(message);
|
||||
}
|
||||
try {
|
||||
this.executor.execute(new TargetTask(message));
|
||||
return true;
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
throw new MessageHandlerRejectedExecutionException(message, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class TargetTask implements Runnable {
|
||||
|
||||
private Message<?> message;
|
||||
|
||||
TargetTask(Message<?> message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
if (!target.send(this.message)) {
|
||||
throw new MessageDeliveryException(message, "failed to send message to target");
|
||||
}
|
||||
}
|
||||
catch (Throwable t) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("error occurred in handler execution", t);
|
||||
}
|
||||
if (errorHandler != null) {
|
||||
errorHandler.handle(t);
|
||||
}
|
||||
else if (logger.isWarnEnabled() && !logger.isDebugEnabled()) {
|
||||
logger.warn("error occurred in handler execution", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.endpoint;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A simple map-backed implementation of {@link EndpointRegistry}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class DefaultEndpointRegistry implements EndpointRegistry {
|
||||
|
||||
private final Map<String, MessageEndpoint> endpoints = new ConcurrentHashMap<String, MessageEndpoint>();
|
||||
|
||||
|
||||
public MessageEndpoint lookupEndpoint(String endpointName) {
|
||||
return this.endpoints.get(endpointName);
|
||||
}
|
||||
|
||||
public void registerEndpoint(String name, MessageEndpoint endpoint) {
|
||||
Assert.notNull(name, "'name' must not be null");
|
||||
Assert.notNull(endpoint, "'endpoint' must not be null");
|
||||
this.endpoints.put(name, endpoint);
|
||||
}
|
||||
|
||||
public MessageEndpoint unregisterEndpoint(String name) {
|
||||
return (name != null) ? this.endpoints.remove(name) : null;
|
||||
}
|
||||
|
||||
public Set<String> getEndpointNames() {
|
||||
return this.endpoints.keySet();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.endpoint;
|
||||
|
||||
/**
|
||||
* A marker interface for endpoint metadata.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public interface EndpointPolicy {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.endpoint;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A strategy interface for registration and lookup of message endpoints by name.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public interface EndpointRegistry {
|
||||
|
||||
void registerEndpoint(String name, MessageEndpoint endpoint);
|
||||
|
||||
MessageEndpoint unregisterEndpoint(String name);
|
||||
|
||||
MessageEndpoint lookupEndpoint(String endpointName);
|
||||
|
||||
Set<String> getEndpointNames();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.endpoint;
|
||||
|
||||
import org.springframework.integration.channel.ChannelRegistry;
|
||||
import org.springframework.integration.channel.ChannelRegistryAware;
|
||||
import org.springframework.integration.channel.MessageChannel;
|
||||
import org.springframework.integration.handler.MessageHandler;
|
||||
import org.springframework.integration.handler.ReplyHandler;
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.message.MessageDeliveryException;
|
||||
import org.springframework.integration.message.MessageHandlingException;
|
||||
import org.springframework.integration.message.MessageHeader;
|
||||
import org.springframework.integration.message.Target;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Implementation of the {@link MessageEndpoint} interface for invoking
|
||||
* {@link MessageHandler MessageHandlers}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class HandlerEndpoint extends TargetEndpoint {
|
||||
|
||||
private volatile MessageHandler handler;
|
||||
|
||||
private volatile ReplyHandler replyHandler = new EndpointReplyHandler();
|
||||
|
||||
private volatile long replyTimeout = 1000;
|
||||
|
||||
private volatile String outputChannelName;
|
||||
|
||||
private volatile boolean returnAddressOverrides = false;
|
||||
|
||||
|
||||
public HandlerEndpoint(MessageHandler handler) {
|
||||
Assert.notNull(handler, "handler must not be null");
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
|
||||
public MessageHandler getHandler() {
|
||||
return this.handler;
|
||||
}
|
||||
|
||||
public void setReplyHandler(ReplyHandler replyHandler) {
|
||||
Assert.notNull(replyHandler, "'replyHandler' must not be null");
|
||||
this.replyHandler = replyHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the timeout in milliseconds to be enforced when this endpoint sends a
|
||||
* reply message. If the message is not sent successfully within the
|
||||
* allotted time, then a MessageDeliveryException will be thrown.
|
||||
* The default <code>replyTimeout</code> value is 1000 milliseconds.
|
||||
*/
|
||||
public void setReplyTimeout(long replyTimeout) {
|
||||
this.replyTimeout = replyTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the channel to which this endpoint should send reply
|
||||
* messages.
|
||||
*/
|
||||
public void setOutputChannelName(String outputChannelName) {
|
||||
this.outputChannelName = outputChannelName;
|
||||
}
|
||||
|
||||
public String getOutputChannelName() {
|
||||
return this.outputChannelName;
|
||||
}
|
||||
|
||||
public void setReturnAddressOverrides(boolean returnAddressOverrides) {
|
||||
this.returnAddressOverrides = returnAddressOverrides;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
Assert.notNull(this.handler, "handler must not be null");
|
||||
if (this.handler instanceof ChannelRegistryAware) {
|
||||
((ChannelRegistryAware) this.handler).setChannelRegistry(this.getChannelRegistry());
|
||||
}
|
||||
super.setTarget(new HandlerInvokingTarget(this.handler, this.replyHandler));
|
||||
super.afterPropertiesSet();
|
||||
}
|
||||
|
||||
private MessageChannel resolveReplyChannel(MessageHeader originalMessageHeader) {
|
||||
if (this.returnAddressOverrides) {
|
||||
MessageChannel channel = this.getReturnAddress(originalMessageHeader);
|
||||
if (channel == null) {
|
||||
channel = this.getOutputChannel();
|
||||
}
|
||||
return channel;
|
||||
}
|
||||
else {
|
||||
MessageChannel channel = this.getOutputChannel();
|
||||
if (channel == null) {
|
||||
channel = this.getReturnAddress(originalMessageHeader);
|
||||
}
|
||||
return channel;
|
||||
}
|
||||
}
|
||||
|
||||
private MessageChannel getReturnAddress(MessageHeader originalMessageHeader) {
|
||||
Object returnAddress = originalMessageHeader.getReturnAddress();
|
||||
if (returnAddress != null) {
|
||||
if (returnAddress instanceof MessageChannel) {
|
||||
return (MessageChannel) returnAddress;
|
||||
}
|
||||
ChannelRegistry registry = this.getChannelRegistry();
|
||||
if (returnAddress instanceof String && registry != null) {
|
||||
String channelName = (String) returnAddress;
|
||||
if (StringUtils.hasText(channelName)) {
|
||||
return registry.lookupChannel(channelName);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private MessageChannel getOutputChannel() {
|
||||
ChannelRegistry registry = this.getChannelRegistry();
|
||||
if (this.outputChannelName != null && registry != null) {
|
||||
return registry.lookupChannel(this.outputChannelName);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private static class HandlerInvokingTarget implements Target {
|
||||
|
||||
private final MessageHandler handler;
|
||||
|
||||
private final ReplyHandler replyHandler;
|
||||
|
||||
|
||||
public HandlerInvokingTarget(MessageHandler handler, ReplyHandler replyHandler) {
|
||||
this.handler = handler;
|
||||
this.replyHandler = replyHandler;
|
||||
}
|
||||
|
||||
|
||||
public boolean send(Message<?> message) {
|
||||
Message<?> replyMessage = this.handler.handle(message);
|
||||
if (replyMessage != null) {
|
||||
if (replyMessage.getHeader().getCorrelationId() == null) {
|
||||
replyMessage.getHeader().setCorrelationId(message.getId());
|
||||
}
|
||||
this.replyHandler.handle(replyMessage, message.getHeader());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private class EndpointReplyHandler implements ReplyHandler {
|
||||
|
||||
public void handle(Message<?> replyMessage, MessageHeader originalMessageHeader) {
|
||||
if (replyMessage == null) {
|
||||
return;
|
||||
}
|
||||
MessageChannel replyChannel = resolveReplyChannel(originalMessageHeader);
|
||||
if (replyChannel == null) {
|
||||
throw new MessageHandlingException(replyMessage, "Unable to determine reply channel for message. " +
|
||||
"Provide an 'outputChannelName' on the message endpoint or a 'returnAddress' in the message header");
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("endpoint '" + HandlerEndpoint.this + "' replying to channel '" + replyChannel + "' with message: " + replyMessage);
|
||||
}
|
||||
if (!replyChannel.send(replyMessage, replyTimeout)) {
|
||||
throw new MessageDeliveryException(replyMessage,
|
||||
"unable to send reply message within alloted timeout of " + replyTimeout + " milliseconds");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.endpoint;
|
||||
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
|
||||
/**
|
||||
* Base interface for message endpoints.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public interface MessageEndpoint extends BeanNameAware {
|
||||
|
||||
String getName();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.endpoint;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.integration.channel.DispatcherPolicy;
|
||||
import org.springframework.integration.channel.MessageChannel;
|
||||
import org.springframework.integration.dispatcher.DefaultPollingDispatcher;
|
||||
import org.springframework.integration.dispatcher.PollingDispatcher;
|
||||
import org.springframework.integration.dispatcher.PollingDispatcherTask;
|
||||
import org.springframework.integration.message.Source;
|
||||
import org.springframework.integration.scheduling.MessagingTask;
|
||||
import org.springframework.integration.scheduling.Schedule;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A channel adapter that retrieves messages from a {@link Source}
|
||||
* and then sends the resulting messages to the provided {@link MessageChannel}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class SourceEndpoint extends AbstractEndpoint implements MessagingTask, InitializingBean {
|
||||
|
||||
private final Schedule schedule;
|
||||
|
||||
private final DispatcherPolicy dispatcherPolicy = new DispatcherPolicy();
|
||||
|
||||
private volatile PollingDispatcher dispatcher;
|
||||
|
||||
private volatile List<Advice> dispatchAdviceChain;
|
||||
|
||||
private volatile MessagingTask task;
|
||||
|
||||
private volatile List<Advice> taskAdviceChain;
|
||||
|
||||
private volatile boolean taskInitialized;
|
||||
|
||||
private final Object taskMonitor = new Object();
|
||||
|
||||
|
||||
public SourceEndpoint(Source<?> source, MessageChannel channel, Schedule schedule) {
|
||||
Assert.notNull(source, "source must not be null");
|
||||
Assert.notNull(channel, "channel must not be null");
|
||||
Assert.notNull(schedule, "schedule must not be null");
|
||||
this.dispatcher = new DefaultPollingDispatcher(source, this.dispatcherPolicy);
|
||||
this.dispatcher.subscribe(channel);
|
||||
this.schedule = schedule;
|
||||
}
|
||||
|
||||
|
||||
public void setMaxMessagesPerTask(int maxMessagesPerTask) {
|
||||
this.dispatcherPolicy.setMaxMessagesPerTask(maxMessagesPerTask);
|
||||
}
|
||||
|
||||
public void setSendTimeout(long sendTimeout) {
|
||||
this.dispatcher.setSendTimeout(sendTimeout);
|
||||
}
|
||||
|
||||
public void setTaskAdviceChain(List<Advice> taskAdviceChain) {
|
||||
this.taskAdviceChain = taskAdviceChain;
|
||||
}
|
||||
|
||||
public void setDispatchAdviceChain(List<Advice> dispatchAdviceChain) {
|
||||
this.dispatchAdviceChain = dispatchAdviceChain;
|
||||
}
|
||||
|
||||
public Schedule getSchedule() {
|
||||
return this.schedule;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
this.initializeTask();
|
||||
}
|
||||
|
||||
public void initializeTask() {
|
||||
synchronized (this.taskMonitor) {
|
||||
if (this.taskInitialized) {
|
||||
return;
|
||||
}
|
||||
this.refreshTask();
|
||||
this.taskInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void refreshTask() {
|
||||
synchronized (this.taskMonitor) {
|
||||
PollingDispatcher dispatcherProxy = null;
|
||||
if (this.dispatchAdviceChain != null && this.dispatchAdviceChain.size() > 0) {
|
||||
ProxyFactory proxyFactory = new ProxyFactory(this.dispatcher);
|
||||
proxyFactory.setInterfaces(new Class[] { PollingDispatcher.class });
|
||||
for (Advice advice : this.dispatchAdviceChain) {
|
||||
proxyFactory.addAdvisor(new MethodNameAdvisor(advice, "dispatch"));
|
||||
}
|
||||
dispatcherProxy = (PollingDispatcher) proxyFactory.getProxy();
|
||||
}
|
||||
this.task = new PollingDispatcherTask((dispatcherProxy != null) ? dispatcherProxy : this.dispatcher, this.schedule);
|
||||
if (this.taskAdviceChain != null && this.taskAdviceChain.size() > 0) {
|
||||
ProxyFactory proxyFactory = new ProxyFactory(this.task);
|
||||
proxyFactory.setInterfaces(new Class[] { MessagingTask.class });
|
||||
for (Advice advice : this.taskAdviceChain) {
|
||||
proxyFactory.addAdvisor(new MethodNameAdvisor(advice, "run"));
|
||||
}
|
||||
this.task = (MessagingTask) proxyFactory.getProxy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private MessagingTask getTask() {
|
||||
synchronized (this.taskMonitor) {
|
||||
if (!this.taskInitialized) {
|
||||
this.initializeTask();
|
||||
}
|
||||
return this.task;
|
||||
}
|
||||
}
|
||||
|
||||
public void run() {
|
||||
this.getTask().run();
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static class MethodNameAdvisor extends StaticMethodMatcherPointcutAdvisor {
|
||||
|
||||
private final String methodName;
|
||||
|
||||
MethodNameAdvisor(Advice advice, String methodName) {
|
||||
super(advice);
|
||||
this.methodName = methodName;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean matches(Method method, Class targetClass) {
|
||||
return method.getName().equals(methodName);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.endpoint;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.integration.channel.ChannelRegistry;
|
||||
import org.springframework.integration.channel.ChannelRegistryAware;
|
||||
import org.springframework.integration.handler.MessageHandlerNotRunningException;
|
||||
import org.springframework.integration.handler.MessageHandlerRejectedExecutionException;
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.message.MessageHandlingException;
|
||||
import org.springframework.integration.message.Target;
|
||||
import org.springframework.integration.message.selector.MessageSelector;
|
||||
import org.springframework.integration.scheduling.Subscription;
|
||||
import org.springframework.integration.util.ErrorHandler;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Base class for {@link MessageEndpoint} implementations to which Messages may be sent.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class TargetEndpoint extends AbstractEndpoint implements Target, ChannelRegistryAware, InitializingBean, Lifecycle {
|
||||
|
||||
private volatile Target target;
|
||||
|
||||
private volatile Subscription subscription;
|
||||
|
||||
private volatile ConcurrencyPolicy concurrencyPolicy;
|
||||
|
||||
private volatile ErrorHandler errorHandler;
|
||||
|
||||
private final List<MessageSelector> selectors = new CopyOnWriteArrayList<MessageSelector>();
|
||||
|
||||
private volatile ChannelRegistry channelRegistry;
|
||||
|
||||
private volatile boolean initialized;
|
||||
|
||||
private volatile boolean running;
|
||||
|
||||
|
||||
public TargetEndpoint() {
|
||||
}
|
||||
|
||||
public TargetEndpoint(Target target) {
|
||||
Assert.notNull(target, "target must not be null");
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
|
||||
public Target getTarget() {
|
||||
return this.target;
|
||||
}
|
||||
|
||||
public void setTarget(Target target) {
|
||||
Assert.notNull(target, "target must not be null");
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public void setMessageSelectors(List<MessageSelector> selectors) {
|
||||
this.selectors.clear();
|
||||
this.selectors.addAll(selectors);
|
||||
}
|
||||
|
||||
public void addMessageSelector(MessageSelector messageSelector) {
|
||||
Assert.notNull(messageSelector, "'messageSelector' must not be null");
|
||||
this.selectors.add(messageSelector);
|
||||
}
|
||||
|
||||
public Subscription getSubscription() {
|
||||
return this.subscription;
|
||||
}
|
||||
|
||||
public void setSubscription(Subscription subscription) {
|
||||
this.subscription = subscription;
|
||||
}
|
||||
|
||||
public ConcurrencyPolicy getConcurrencyPolicy() {
|
||||
return this.concurrencyPolicy;
|
||||
}
|
||||
|
||||
public void setConcurrencyPolicy(ConcurrencyPolicy concurrencyPolicy) {
|
||||
this.concurrencyPolicy = concurrencyPolicy;
|
||||
}
|
||||
|
||||
public void setErrorHandler(ErrorHandler errorHandler) {
|
||||
this.errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
public boolean hasErrorHandler() {
|
||||
return (this.errorHandler != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the channel registry to use for looking up channels by name.
|
||||
*/
|
||||
public void setChannelRegistry(ChannelRegistry channelRegistry) {
|
||||
this.channelRegistry = channelRegistry;
|
||||
}
|
||||
|
||||
protected ChannelRegistry getChannelRegistry() {
|
||||
return this.channelRegistry;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
if (this.target instanceof ChannelRegistryAware && this.channelRegistry != null) {
|
||||
((ChannelRegistryAware) this.target).setChannelRegistry(this.channelRegistry);
|
||||
}
|
||||
if (this.concurrencyPolicy != null && !(this.target instanceof ConcurrentTarget)) {
|
||||
int capacity = this.concurrencyPolicy.getQueueCapacity();
|
||||
BlockingQueue<Runnable> queue = (capacity < 1) ? new SynchronousQueue<Runnable>() : new ArrayBlockingQueue<Runnable>(capacity);
|
||||
ExecutorService executor = new ThreadPoolExecutor(this.concurrencyPolicy.getCoreSize(), this.concurrencyPolicy.getMaxSize(),
|
||||
this.concurrencyPolicy.getKeepAliveSeconds(), TimeUnit.SECONDS, queue);
|
||||
this.target = new ConcurrentTarget(this.target, executor);
|
||||
}
|
||||
if (this.target instanceof ConcurrentTarget) {
|
||||
if (this.errorHandler != null) {
|
||||
((ConcurrentTarget) this.target).setErrorHandler(this.errorHandler);
|
||||
}
|
||||
}
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return this.running;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
if (this.isRunning()) {
|
||||
return;
|
||||
}
|
||||
if (!this.initialized) {
|
||||
this.afterPropertiesSet();
|
||||
}
|
||||
this.running = true;
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (!this.isRunning()) {
|
||||
return;
|
||||
}
|
||||
if (this.target instanceof DisposableBean) {
|
||||
try {
|
||||
((DisposableBean) this.target).destroy();
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("exception occurred when destroying target", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.running = false;
|
||||
}
|
||||
|
||||
public final boolean send(Message<?> message) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("endpoint '" + this + "' handling message: " + message);
|
||||
}
|
||||
if (!this.isRunning()) {
|
||||
throw new MessageHandlerNotRunningException(message);
|
||||
}
|
||||
for (MessageSelector selector : this.selectors) {
|
||||
if (!selector.accept(message)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
try {
|
||||
return this.target.send(message);
|
||||
}
|
||||
catch (MessageHandlerRejectedExecutionException e) {
|
||||
throw e;
|
||||
}
|
||||
catch (Throwable t) {
|
||||
if (this.errorHandler == null) {
|
||||
if (t instanceof MessageHandlingException) {
|
||||
throw (MessageHandlingException) t;
|
||||
}
|
||||
throw new MessageHandlingException(message,
|
||||
"error occurred in endpoint, and no 'errorHandler' available", t);
|
||||
}
|
||||
this.errorHandler.handle(t);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.gateway;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.beans.SimpleTypeConverter;
|
||||
import org.springframework.beans.TypeConverter;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.integration.bus.MessageBus;
|
||||
import org.springframework.integration.config.MessageBusParser;
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Generates a proxy for the provided service interface to enable interaction
|
||||
* with messaging components without application code being aware of them.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class GatewayProxyFactoryBean extends SimpleMessagingGateway
|
||||
implements FactoryBean, MethodInterceptor, InitializingBean, BeanClassLoaderAware, BeanFactoryAware {
|
||||
|
||||
private Class<?> serviceInterface;
|
||||
|
||||
private TypeConverter typeConverter = new SimpleTypeConverter();
|
||||
|
||||
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
|
||||
|
||||
private Object serviceProxy;
|
||||
|
||||
|
||||
public void setServiceInterface(Class<?> serviceInterface) {
|
||||
this.serviceInterface = serviceInterface;
|
||||
}
|
||||
|
||||
public void setTypeConverter(TypeConverter typeConverter) {
|
||||
Assert.notNull(typeConverter, "typeConverter must not be null");
|
||||
this.typeConverter = typeConverter;
|
||||
}
|
||||
|
||||
public void setBeanClassLoader(ClassLoader beanClassLoader) {
|
||||
this.beanClassLoader = beanClassLoader;
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
this.getRequestReplyTemplate().setMessageBus(
|
||||
(MessageBus) beanFactory.getBean(MessageBusParser.MESSAGE_BUS_BEAN_NAME));
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
this.serviceProxy = new ProxyFactory(this.serviceInterface, this).getProxy(this.beanClassLoader);
|
||||
}
|
||||
|
||||
public Object getObject() throws Exception {
|
||||
return this.serviceProxy;
|
||||
}
|
||||
|
||||
public Class<?> getObjectType() {
|
||||
return this.serviceInterface;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public Object invoke(MethodInvocation invocation) throws Throwable {
|
||||
Class<?> returnType = invocation.getMethod().getReturnType();
|
||||
boolean shouldReturnMessage = Message.class.isAssignableFrom(returnType);
|
||||
int paramCount = invocation.getMethod().getParameterTypes().length;
|
||||
Object response = null;
|
||||
if (paramCount == 0) {
|
||||
if (shouldReturnMessage) {
|
||||
return this.receive();
|
||||
}
|
||||
response = this.receive();
|
||||
}
|
||||
else {
|
||||
Object payload = (paramCount == 1) ? invocation.getArguments()[0] : invocation.getArguments();
|
||||
if (returnType.equals(void.class)) {
|
||||
this.send(payload);
|
||||
return null;
|
||||
}
|
||||
response = shouldReturnMessage ? this.sendAndReceiveMessage(payload) : this.sendAndReceive(payload);
|
||||
}
|
||||
return (response != null) ? this.typeConverter.convertIfNecessary(response, returnType) : null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.gateway;
|
||||
|
||||
import org.springframework.integration.channel.MessageChannel;
|
||||
import org.springframework.integration.channel.RequestReplyTemplate;
|
||||
|
||||
/**
|
||||
* A convenient base class providing access to a {@link RequestReplyTemplate} and exposing setter methods for
|
||||
* configuring request and reply {@link MessageChannel MessageChannels}. May be used as a base class for framework
|
||||
* components so that the details of messaging are well-encapsulated and hidden from application code. For example,
|
||||
* see {@link SimpleMessagingGateway}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public abstract class MessagingGatewaySupport {
|
||||
|
||||
private final RequestReplyTemplate requestReplyTemplate = new RequestReplyTemplate();
|
||||
|
||||
|
||||
public MessagingGatewaySupport(MessageChannel requestChannel) {
|
||||
this.requestReplyTemplate.setRequestChannel(requestChannel);
|
||||
}
|
||||
|
||||
public MessagingGatewaySupport() {
|
||||
super();
|
||||
}
|
||||
|
||||
|
||||
public void setRequestChannel(MessageChannel requestChannel) {
|
||||
this.requestReplyTemplate.setRequestChannel(requestChannel);
|
||||
}
|
||||
|
||||
public void setReplyChannel(MessageChannel replyChannel) {
|
||||
this.requestReplyTemplate.setReplyChannel(replyChannel);
|
||||
}
|
||||
|
||||
public void setRequestTimeout(long requestTimeout) {
|
||||
this.requestReplyTemplate.setRequestTimeout(requestTimeout);
|
||||
}
|
||||
|
||||
public void setReplyTimeout(long replyTimeout) {
|
||||
this.requestReplyTemplate.setReplyTimeout(replyTimeout);
|
||||
}
|
||||
|
||||
protected final RequestReplyTemplate getRequestReplyTemplate() {
|
||||
return this.requestReplyTemplate;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.gateway;
|
||||
|
||||
import org.springframework.integration.channel.MessageChannel;
|
||||
import org.springframework.integration.message.DefaultMessageCreator;
|
||||
import org.springframework.integration.message.DefaultMessageMapper;
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.message.MessageCreator;
|
||||
import org.springframework.integration.message.MessageDeliveryException;
|
||||
import org.springframework.integration.message.MessageMapper;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A general purpose class that supports a variety of message exchanges. Useful for connecting application code to
|
||||
* {@link MessageChannel MessageChannels} for sending, receiving, or request-reply operations. The sending methods
|
||||
* accept any Object as the parameter value (i.e. it is not required to be a Message). A custom {@link MessageCreator}
|
||||
* may be provided for creating Messages from the Objects. Likewise return values may be any Object and a custom
|
||||
* implementation of the {@link MessageMapper} strategy may be provided for mapping a reply Message to an Object.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class SimpleMessagingGateway extends MessagingGatewaySupport {
|
||||
|
||||
private MessageCreator messageCreator = new DefaultMessageCreator();
|
||||
|
||||
private MessageMapper messageMapper = new DefaultMessageMapper();
|
||||
|
||||
|
||||
public SimpleMessagingGateway(MessageChannel requestChannel) {
|
||||
super(requestChannel);
|
||||
}
|
||||
|
||||
public SimpleMessagingGateway() {
|
||||
super();
|
||||
}
|
||||
|
||||
|
||||
public void setMessageCreator(MessageCreator<?, ?> messageCreator) {
|
||||
Assert.notNull(messageCreator, "messageCreator must not be null");
|
||||
this.messageCreator = messageCreator;
|
||||
}
|
||||
|
||||
public void setMessageMapper(MessageMapper<?, ?> messageMapper) {
|
||||
Assert.notNull(messageMapper, "messageMapper must not be null");
|
||||
this.messageMapper = messageMapper;
|
||||
}
|
||||
|
||||
public void send(Object object) {
|
||||
Message<?> message = (object instanceof Message) ? (Message) object :
|
||||
this.messageCreator.createMessage(object);
|
||||
if (message != null) {
|
||||
if (!this.getRequestReplyTemplate().send(message)) {
|
||||
throw new MessageDeliveryException(message, "failed to send Message to channel");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Object receive() {
|
||||
Message<?> message = this.getRequestReplyTemplate().receive();
|
||||
return (message != null) ? this.messageMapper.mapMessage(message) : null;
|
||||
}
|
||||
|
||||
public Object sendAndReceive(Object object) {
|
||||
return this.sendAndReceive(object, true);
|
||||
}
|
||||
|
||||
public Message<?> sendAndReceiveMessage(Object object) {
|
||||
return (Message<?>) this.sendAndReceive(object, false);
|
||||
}
|
||||
|
||||
private Object sendAndReceive(Object object, boolean shouldMapMessage) {
|
||||
Message<?> request = (object instanceof Message) ? (Message) object :
|
||||
this.messageCreator.createMessage(object);
|
||||
if (request == null) {
|
||||
return null;
|
||||
}
|
||||
Message<?> reply = this.getRequestReplyTemplate().request(request);
|
||||
if (!shouldMapMessage) {
|
||||
return reply;
|
||||
}
|
||||
return (reply != null) ? this.messageMapper.mapMessage(reply) : null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.handler;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.integration.ConfigurationException;
|
||||
import org.springframework.integration.message.DefaultMessageCreator;
|
||||
import org.springframework.integration.message.DefaultMessageMapper;
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.message.MessageCreator;
|
||||
import org.springframework.integration.message.MessageMapper;
|
||||
import org.springframework.integration.message.MessagingException;
|
||||
import org.springframework.integration.util.DefaultMethodInvoker;
|
||||
import org.springframework.integration.util.MethodInvoker;
|
||||
import org.springframework.integration.util.NameResolvingMethodInvoker;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* An implementation of the {@link MessageHandler} interface that invokes the specified method and target object. Either
|
||||
* a {@link Method} reference or a 'methodName' may be provided, but both are not necessary. In fact, while preference
|
||||
* is given to a {@link Method} reference if available, an Exception will be thrown if a non-matching 'methodName' has
|
||||
* also been provided. Therefore, to avoid such ambiguity, it is recommended to provide just one or the other.
|
||||
* <p>
|
||||
* This handler also accepts an implementation of the {@link MessageMapper} strategy interface which it uses for
|
||||
* converting from the {@link Message} being handled to an Object prior to invoking the method. Likewise, if the method
|
||||
* has a non-null return value, a reply message will be generated by the configured implementation of the
|
||||
* {@link MessageCreator} strategy interface. In both cases, the default implementations will simply consider the
|
||||
* message's payload.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public abstract class AbstractMessageHandlerAdapter implements MessageHandler, InitializingBean {
|
||||
|
||||
public static final String OUTPUT_CHANNEL_NAME_KEY = "outputChannelName";
|
||||
|
||||
|
||||
protected final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
private volatile Object object;
|
||||
|
||||
private volatile Method method;
|
||||
|
||||
private volatile String methodName;
|
||||
|
||||
private volatile boolean methodExpectsMessage;
|
||||
|
||||
private volatile MessageMapper messageMapper = new DefaultMessageMapper();
|
||||
|
||||
private volatile MessageCreator messageCreator = new DefaultMessageCreator();
|
||||
|
||||
protected volatile MethodInvoker invoker;
|
||||
|
||||
private volatile boolean initialized;
|
||||
|
||||
private final Object initializationMonitor = new Object();
|
||||
|
||||
|
||||
public void setObject(Object object) {
|
||||
this.object = object;
|
||||
}
|
||||
|
||||
protected Object getObject() {
|
||||
return this.object;
|
||||
}
|
||||
|
||||
public void setMethod(Method method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
protected Method getMethod() {
|
||||
return this.method;
|
||||
}
|
||||
|
||||
public void setMethodName(String methodName) {
|
||||
this.methodName = methodName;
|
||||
}
|
||||
|
||||
public void setMethodExpectsMessage(boolean methodExpectsMessage) {
|
||||
this.methodExpectsMessage = methodExpectsMessage;
|
||||
}
|
||||
|
||||
public void setMessageMapper(MessageMapper messageMapper) {
|
||||
Assert.notNull(messageMapper, "'messageMapper' must not be null");
|
||||
this.messageMapper = messageMapper;
|
||||
}
|
||||
|
||||
public void setMessageCreator(MessageCreator messageCreator) {
|
||||
Assert.notNull(messageCreator, "'messageCreator' must not be null");
|
||||
this.messageCreator = messageCreator;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
synchronized (this.initializationMonitor) {
|
||||
if (this.initialized) {
|
||||
return;
|
||||
}
|
||||
if (this.object == null) {
|
||||
throw new ConfigurationException("The target 'object' must not be null.");
|
||||
}
|
||||
if (this.method == null && this.methodName == null) {
|
||||
throw new ConfigurationException("Either a 'method' or 'methodName' is required.");
|
||||
}
|
||||
if (this.method != null) {
|
||||
if (this.methodName != null && !this.methodName.equals(this.method.getName())) {
|
||||
throw new ConfigurationException("An ambiguity exists between the 'method' and 'methodName' properties. " +
|
||||
"Note that only one of them is required, but if both are provided they must match.");
|
||||
}
|
||||
this.invoker = new DefaultMethodInvoker(this.object, this.method);
|
||||
}
|
||||
else {
|
||||
this.invoker = new NameResolvingMethodInvoker(this.object, this.methodName);
|
||||
}
|
||||
this.initialized = true;
|
||||
}
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses may override this method for custom initialization requirements.
|
||||
*/
|
||||
protected void initialize() {
|
||||
}
|
||||
|
||||
public Message<?> handle(Message<?> message) {
|
||||
if (message == null) {
|
||||
throw new IllegalArgumentException("message must not be null");
|
||||
}
|
||||
if (!this.initialized) {
|
||||
this.afterPropertiesSet();
|
||||
}
|
||||
Object args[] = null;
|
||||
Object mappingResult = (this.methodExpectsMessage) ? message : this.messageMapper.mapMessage(message);
|
||||
if (mappingResult != null && mappingResult.getClass().isArray()) {
|
||||
args = (Object[]) mappingResult;
|
||||
}
|
||||
else {
|
||||
args = new Object[] { mappingResult };
|
||||
}
|
||||
try {
|
||||
Object result = null;
|
||||
try {
|
||||
result = this.invoker.invokeMethod(args);
|
||||
}
|
||||
catch (NoSuchMethodException e) {
|
||||
result = this.invoker.invokeMethod(message);
|
||||
this.methodExpectsMessage = true;
|
||||
}
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
return this.handleReturnValue(result, message);
|
||||
}
|
||||
catch (InvocationTargetException e) {
|
||||
throw new MessagingException(
|
||||
"Handler method '" + this.method + "' threw an Exception.", e.getTargetException());
|
||||
}
|
||||
catch (Throwable e) {
|
||||
throw new MessagingException("Failed to invoke handler method '" + this.method +
|
||||
"' with arguments: " + ObjectUtils.nullSafeToString(args), e);
|
||||
}
|
||||
}
|
||||
|
||||
protected Message<?> createReplyMessage(Object returnValue, Message<?> originalMessage) {
|
||||
Message<?> reply = this.messageCreator.createMessage(returnValue);
|
||||
if (reply != null) {
|
||||
reply.copyHeader(originalMessage.getHeader(), false);
|
||||
Object correlationId = reply.getHeader().getCorrelationId();
|
||||
if (correlationId == null) {
|
||||
Object orginalCorrelationId = originalMessage.getHeader().getCorrelationId();
|
||||
reply.getHeader().setCorrelationId((orginalCorrelationId != null) ?
|
||||
orginalCorrelationId : originalMessage.getId());
|
||||
}
|
||||
}
|
||||
return reply;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses must implement this method to handle the return value.
|
||||
*/
|
||||
protected abstract Message<?> handleReturnValue(Object returnValue, Message<?> originalMessage);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.handler;
|
||||
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.message.MessageCreator;
|
||||
|
||||
/**
|
||||
* An implementation of {@link MessageHandler} that invokes the specified method and target object.
|
||||
* It will use the provided implementation of the {@link MessageCreator} strategy interface to convert
|
||||
* the method invocation's return value to a reply Message.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class DefaultMessageHandlerAdapter extends AbstractMessageHandlerAdapter {
|
||||
|
||||
protected Message<?> handleReturnValue(Object returnValue, Message<?> originalMessage) {
|
||||
return this.createReplyMessage(returnValue, originalMessage);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.handler;
|
||||
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A message handler implementation that intercepts calls to another handler.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public abstract class InterceptingMessageHandler implements MessageHandler {
|
||||
|
||||
private MessageHandler target;
|
||||
|
||||
|
||||
public InterceptingMessageHandler(MessageHandler target) {
|
||||
Assert.notNull(target, "target must not be null");
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public final Message<?> handle(Message<?> message) {
|
||||
return handle(message, this.target);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The handler method for subclasses to implement.
|
||||
*
|
||||
* @param message the message to handle
|
||||
* @param target the intercepted handler
|
||||
* @return a reply message or null
|
||||
*/
|
||||
public abstract Message<?> handle(Message<?> message, MessageHandler target);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.handler;
|
||||
|
||||
import org.springframework.integration.message.Message;
|
||||
|
||||
/**
|
||||
* Base message handler interface. Typical implementations will translate
|
||||
* between the generic Messages of the integration framework and the domain
|
||||
* objects that are passed-to and returned-from business components.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public interface MessageHandler {
|
||||
|
||||
Message<?> handle(Message<?> message);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.handler;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import org.springframework.integration.message.Message;
|
||||
|
||||
/**
|
||||
* A message handler implementation that passes incoming messages through a
|
||||
* chain of handlers. The chain will be broken by any handler throwing an
|
||||
* exception or returning <em>null</em>.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class MessageHandlerChain implements MessageHandler {
|
||||
|
||||
private final List<MessageHandler> handlers = new CopyOnWriteArrayList<MessageHandler>();
|
||||
|
||||
|
||||
/**
|
||||
* Add a handler to the end of the chain.
|
||||
*/
|
||||
public void add(MessageHandler handler) {
|
||||
this.handlers.add(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a handler to the chain at the specified index.
|
||||
*/
|
||||
public void add(int index, MessageHandler handler) {
|
||||
this.handlers.add(index, handler);
|
||||
}
|
||||
|
||||
public void setHandlers(List<MessageHandler> handlers) {
|
||||
this.handlers.clear();
|
||||
this.handlers.addAll(handlers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an immutable list of handlers
|
||||
*/
|
||||
public List<MessageHandler> getHandlers() {
|
||||
return Collections.unmodifiableList(handlers);
|
||||
}
|
||||
|
||||
public final Message<?> handle(Message<?> message) {
|
||||
for (MessageHandler next : handlers) {
|
||||
message = next.handle(message);
|
||||
if (message == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.handler;
|
||||
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.message.MessageHandlingException;
|
||||
|
||||
/**
|
||||
* An exception indicating that a handler is not currently running.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class MessageHandlerNotRunningException extends MessageHandlingException {
|
||||
|
||||
public MessageHandlerNotRunningException(Message<?> message) {
|
||||
super(message, "handler is not running");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.handler;
|
||||
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.message.MessageHandlingException;
|
||||
|
||||
/**
|
||||
* An exception indicating that a message was rejected by a handler; typically
|
||||
* this would be the result of a thread pool executor rejecting a handler task.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class MessageHandlerRejectedExecutionException extends MessageHandlingException {
|
||||
|
||||
public MessageHandlerRejectedExecutionException(Message<?> message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public MessageHandlerRejectedExecutionException(Message<?> message, Throwable cause) {
|
||||
super(message, "handler rejected execution", cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.handler;
|
||||
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.message.MessagingException;
|
||||
import org.springframework.integration.message.Target;
|
||||
|
||||
/**
|
||||
* A messaging target that invokes the specified method on the provided object.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class MethodInvokingTarget extends AbstractMessageHandlerAdapter implements Target {
|
||||
|
||||
public boolean send(Message<?> message) {
|
||||
this.handle(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Message<?> handleReturnValue(Object returnValue, Message<?> originalMessage) {
|
||||
if (returnValue != null) {
|
||||
throw new MessagingException(originalMessage, "The target method returned a non-null Object. " +
|
||||
"MethodInvokingTarget should only be used for methods that return no value (preferably void).");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.handler;
|
||||
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.message.MessageHeader;
|
||||
|
||||
/**
|
||||
* Strategy interface for handling reply messages.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public interface ReplyHandler {
|
||||
|
||||
void handle(Message<?> replyMessage, MessageHeader originalMessageHeader);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.handler;
|
||||
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.message.MessageHandlingException;
|
||||
import org.springframework.integration.message.RetrievalBlockingMessageStore;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A handler for receiving messages from a "reply channel". Any component that
|
||||
* is expecting a reply message can poll by providing the correlation identifier.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class ReplyMessageCorrelator implements MessageHandler {
|
||||
|
||||
private volatile long defaultTimeout = 5000;
|
||||
|
||||
private final RetrievalBlockingMessageStore messageStore;
|
||||
|
||||
|
||||
public ReplyMessageCorrelator(int capacity) {
|
||||
this.messageStore = new RetrievalBlockingMessageStore(capacity);
|
||||
}
|
||||
|
||||
|
||||
public void setDefaultTimeout(long defaultTimeout) {
|
||||
Assert.isTrue(defaultTimeout >= 0, "'defaultTimeout' must not be negative");
|
||||
this.defaultTimeout = defaultTimeout;
|
||||
}
|
||||
|
||||
public Message<?> handle(Message<?> message) {
|
||||
Object correlationId = this.getCorrelationId(message);
|
||||
if (correlationId == null) {
|
||||
throw new MessageHandlingException(message,
|
||||
"unable to handle response, message has no correlationId: " + message);
|
||||
}
|
||||
this.messageStore.put(correlationId, message);
|
||||
return null;
|
||||
}
|
||||
|
||||
public Message<?> getReply(Object correlationId) {
|
||||
return this.getReply(correlationId, this.defaultTimeout);
|
||||
}
|
||||
|
||||
public Message<?> getReply(Object correlationId, long timeout) {
|
||||
Assert.notNull(correlationId, "'correlationId' must not be null");
|
||||
return this.messageStore.remove(correlationId, timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the correlation identifier from the provided message.
|
||||
* <p>
|
||||
* This method may be overridden by subclasses. The default implementation
|
||||
* returns the 'correlationId' from the message header.
|
||||
*/
|
||||
protected Object getCorrelationId(final Message<?> message) {
|
||||
return message.getHeader().getCorrelationId();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.handler.annotation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.integration.ConfigurationException;
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.message.MessageHandlingException;
|
||||
import org.springframework.integration.message.MessageHeader;
|
||||
import org.springframework.integration.message.MessageMapper;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A {@link MessageMapper} implementation for annotated handler methods.
|
||||
* Method parameters are matched against the Message payload as well as its
|
||||
* header attributes and properties. If a method parameter is annotated with
|
||||
* {@link HeaderAttribute @HeaderAttribute} or {@link HeaderProperty @HeaderProperty},
|
||||
* the annotation's value will be used as an attribute/property key. If such an
|
||||
* annotation contains no value, then the parameter name will be used as long as
|
||||
* the information is available in the class file (requires compilation with
|
||||
* debug settings for parameter names). If neither annotation is present, then
|
||||
* the parameter will typically match the Message payload. However, if a Map or
|
||||
* Properties object is expected, and the paylaod is not itself assignable to
|
||||
* that type, then the MessageHeader attributes will be passed in the case of
|
||||
* a Map-typed parameter, or the MessageHeader properties will be passed in the
|
||||
* case of a Properties-typed parameter.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class AnnotationMethodMessageMapper implements MessageMapper {
|
||||
|
||||
private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
|
||||
|
||||
private final Method method;
|
||||
|
||||
private MethodParameterMetadata[] parameterMetadata;
|
||||
|
||||
private volatile boolean initialized;
|
||||
|
||||
private final Object initializationMonitor = new Object();
|
||||
|
||||
|
||||
public AnnotationMethodMessageMapper(Method method) {
|
||||
Assert.notNull(method, "method must not be null");
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
|
||||
public void initialize() {
|
||||
synchronized (this.initializationMonitor) {
|
||||
if (this.initialized) {
|
||||
return;
|
||||
}
|
||||
Class<?>[] paramTypes = this.method.getParameterTypes();
|
||||
this.parameterMetadata = new MethodParameterMetadata[paramTypes.length];
|
||||
for (int i = 0; i < parameterMetadata.length; i++) {
|
||||
MethodParameter methodParam = new MethodParameter(this.method, i);
|
||||
methodParam.initParameterNameDiscovery(this.parameterNameDiscoverer);
|
||||
GenericTypeResolver.resolveParameterType(methodParam, this.method.getDeclaringClass());
|
||||
Object[] paramAnns = methodParam.getParameterAnnotations();
|
||||
String attributeName = null;
|
||||
String propertyName = null;
|
||||
for (int j = 0; j < paramAnns.length; j++) {
|
||||
Object paramAnn = paramAnns[j];
|
||||
if (HeaderAttribute.class.isInstance(paramAnn)) {
|
||||
HeaderAttribute headerAttribute = (HeaderAttribute) paramAnn;
|
||||
attributeName = this.resolveParameterNameIfNecessary(headerAttribute.value(), methodParam);
|
||||
parameterMetadata[i] = new MethodParameterMetadata(HeaderAttribute.class, attributeName, headerAttribute.required());
|
||||
}
|
||||
else if (HeaderProperty.class.isInstance(paramAnn)) {
|
||||
HeaderProperty headerProperty = (HeaderProperty) paramAnn;
|
||||
propertyName = this.resolveParameterNameIfNecessary(headerProperty.value(), methodParam);
|
||||
parameterMetadata[i] = new MethodParameterMetadata(HeaderProperty.class, propertyName, headerProperty.required());
|
||||
}
|
||||
}
|
||||
if (attributeName != null && propertyName != null) {
|
||||
throw new ConfigurationException("The @HeaderAttribute and @HeaderProperty annotations " +
|
||||
"are mutually exclusive. They should not both be provided on the same parameter.");
|
||||
}
|
||||
if (attributeName == null && propertyName == null) {
|
||||
parameterMetadata[i] = new MethodParameterMetadata(methodParam.getParameterType(), null, false);
|
||||
}
|
||||
}
|
||||
this.initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
public Object[] mapMessage(Message message) {
|
||||
if (message == null) {
|
||||
return null;
|
||||
}
|
||||
if (message.getPayload() == null) {
|
||||
throw new IllegalArgumentException("Message payload must not be null.");
|
||||
}
|
||||
if (!this.initialized) {
|
||||
this.initialize();
|
||||
}
|
||||
Object[] args = new Object[this.parameterMetadata.length];
|
||||
for (int i = 0; i < this.parameterMetadata.length; i++) {
|
||||
MethodParameterMetadata metadata = this.parameterMetadata[i];
|
||||
Class<?> expectedType = metadata.type;
|
||||
if (expectedType.equals(HeaderAttribute.class)) {
|
||||
Object value = message.getHeader().getAttribute(metadata.key);
|
||||
if (value == null && metadata.required) {
|
||||
throw new MessageHandlingException(message,
|
||||
"required attribute '" + metadata.key + "' not available");
|
||||
}
|
||||
args[i] = value;
|
||||
}
|
||||
else if (expectedType.equals(HeaderProperty.class)) {
|
||||
Object value = message.getHeader().getProperty(metadata.key);
|
||||
if (value == null && metadata.required) {
|
||||
throw new MessageHandlingException(message,
|
||||
"required property '" + metadata.key + "' not available");
|
||||
}
|
||||
args[i] = value;
|
||||
}
|
||||
else if (expectedType.isAssignableFrom(message.getClass())) {
|
||||
args[i] = message;
|
||||
}
|
||||
else if (expectedType.isAssignableFrom(message.getPayload().getClass())) {
|
||||
args[i] = message.getPayload();
|
||||
}
|
||||
else if (expectedType.equals(Map.class)) {
|
||||
args[i] = this.getHeaderAttributes(message);
|
||||
}
|
||||
else if (expectedType.equals(Properties.class)) {
|
||||
args[i] = this.getHeaderProperties(message);
|
||||
}
|
||||
else {
|
||||
args[i] = message.getPayload();
|
||||
}
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
private Map<String, Object> getHeaderAttributes(Message<?> message) {
|
||||
Map<String, Object> attributes = new HashMap<String, Object>();
|
||||
MessageHeader header = message.getHeader();
|
||||
Set<String> attributeNames = header.getAttributeNames();
|
||||
for (String name : attributeNames) {
|
||||
attributes.put(name, header.getAttribute(name));
|
||||
}
|
||||
return attributes;
|
||||
}
|
||||
|
||||
private Properties getHeaderProperties(Message<?> message) {
|
||||
Properties properties = new Properties();
|
||||
MessageHeader header = message.getHeader();
|
||||
Set<String> propertyNames = header.getPropertyNames();
|
||||
for (String name : propertyNames) {
|
||||
properties.setProperty(name, header.getProperty(name));
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
private String resolveParameterNameIfNecessary(String paramName, MethodParameter methodParam) {
|
||||
if (!StringUtils.hasText(paramName)) {
|
||||
paramName = methodParam.getParameterName();
|
||||
if (paramName == null) {
|
||||
throw new IllegalStateException("No parameter name specified and not available in class file.");
|
||||
}
|
||||
}
|
||||
return paramName;
|
||||
}
|
||||
|
||||
|
||||
private static class MethodParameterMetadata {
|
||||
|
||||
private final Class<?> type;
|
||||
|
||||
private final String key;
|
||||
|
||||
private final boolean required;
|
||||
|
||||
|
||||
MethodParameterMetadata(Class<?> type, String key, boolean required) {
|
||||
this.type = type;
|
||||
this.key = key;
|
||||
this.required = required;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.handler.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation indicating that a method parameter's value should be
|
||||
* retrieved from an attribute in the message header. The value of
|
||||
* the annotation provides the attribute key, and the optional
|
||||
* 'required' property specifies whether the attribute value must
|
||||
* be available within the header.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
@Target(ElementType.PARAMETER)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface HeaderAttribute {
|
||||
|
||||
String value() default "";
|
||||
|
||||
boolean required() default true;
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user