/*
* Copyright (C) 2011 Red Hat, Inc. and/or its affiliates.
*
* 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.jboss.errai.bus.client.api.base;
import java.util.Map;
import org.jboss.errai.bus.client.api.RoutingFlag;
import org.jboss.errai.bus.client.api.messaging.Message;
import org.jboss.errai.bus.client.api.messaging.MessageBus;
import org.jboss.errai.bus.client.api.messaging.RequestDispatcher;
import org.jboss.errai.common.client.api.Assert;
import org.jboss.errai.common.client.api.ErrorCallback;
import org.jboss.errai.common.client.api.ResourceProvider;
import org.jboss.errai.common.client.protocols.MessageParts;
/**
* Internal wrapper class that makes any type of given message (the <i>wrapped
* message</i>) conversational.
*/
class ConversationMessageWrapper implements Message {
protected Message message;
protected Message newMessage;
/**
* Creates a new wrapper that makes newMessage a reply to the given message.
*
* @param inReplyTo
* The message this wrapper is in reply to. Not null.
* @param newMessage
* The new message to be wrapped. Not null.
*/
public ConversationMessageWrapper(final Message inReplyTo, final Message newMessage) {
this.message = Assert.notNull(inReplyTo);
this.newMessage = Assert.notNull(newMessage);
}
@Override
public Message toSubject(final String subject) {
newMessage.toSubject(subject);
return this;
}
@Override
public String getSubject() {
return newMessage.getSubject();
}
@Override
public Message command(final String type) {
newMessage.command(type);
return this;
}
@Override
public Message command(final Enum<?> type) {
newMessage.command(type);
return this;
}
@Override
public String getCommandType() {
return newMessage.getCommandType();
}
@Override
public Message set(final String part, final Object value) {
newMessage.set(part, value);
return this;
}
@Override
public Message set(final Enum<?> part, final Object value) {
newMessage.set(part, value);
return this;
}
@Override
public Message setProvidedPart(final String part, final ResourceProvider<?> provider) {
newMessage.setProvidedPart(part, provider);
return this;
}
@Override
public Message setProvidedPart(final Enum<?> part, final ResourceProvider<?> provider) {
newMessage.setProvidedPart(part, provider);
return this;
}
@Override
public boolean hasPart(final String part) {
return newMessage.hasPart(part);
}
@Override
public boolean hasPart(final Enum<?> part) {
return newMessage.hasPart(part);
}
@Override
public void remove(final String part) {
newMessage.remove(part);
}
@Override
public void remove(final Enum<?> part) {
newMessage.remove(part);
}
@Override
public Message copy(final String part, final Message m) {
newMessage.copy(part, m);
return this;
}
@Override
public Message copy(final Enum<?> part, final Message m) {
newMessage.copy(part, m);
return this;
}
@Override
public Message setParts(final Map<String, Object> parts) {
newMessage.setParts(parts);
return this;
}
@Override
public Message addAllParts(final Map<String, Object> parts) {
newMessage.addAllParts(parts);
return this;
}
@Override
public Message addAllProvidedParts(final Map<String, ResourceProvider<?>> provided) {
newMessage.addAllProvidedParts(provided);
return this;
}
@Override
public Map<String, ResourceProvider<?>> getProvidedParts() {
return newMessage.getProvidedParts();
}
@Override
public Map<String, Object> getParts() {
return newMessage.getParts();
}
@Override
public void addResources(final Map<String, ?> resources) {
newMessage.addResources(resources);
}
@Override
public Message setResource(final String key, final Object res) {
newMessage.setResource(key, res);
return this;
}
@Override
public <T> T getResource(final Class<T> type, final String key) {
return newMessage.getResource(type, key);
}
@Override
public boolean hasResource(final String key) {
return newMessage.hasResource(key);
}
@Override
public Message copyResource(final String key, final Message m) {
newMessage.copyResource(key, m);
return this;
}
@Override
public Message errorsCall(final ErrorCallback callback) {
newMessage.errorsCall(callback);
return this;
}
@Override
public ErrorCallback<Message> getErrorCallback() {
return newMessage.getErrorCallback();
}
@Override
public <T> T getValue(final Class<T> type) {
return newMessage.getValue(type);
}
@Override
public <T> T get(final Class<T> type, final String part) {
return newMessage.get(type, part);
}
@Override
public <T> T get(final Class<T> type, final Enum<?> part) {
return newMessage.get(type, part);
}
@Override
public Message setFlag(final RoutingFlag flag) {
newMessage.setFlag(flag);
return this;
}
@Override
public void unsetFlag(final RoutingFlag flag) {
newMessage.unsetFlag(flag);
}
@Override
public boolean isFlagSet(final RoutingFlag flag) {
return newMessage.isFlagSet(flag);
}
@Override
public void sendNowWith(final MessageBus viaThis) {
if (ConversationHelper.hasConversationCallback(this)) {
ConversationHelper.createConversationService(viaThis, this);
}
viaThis.send(this);
}
@Override
public void sendNowWith(final RequestDispatcher viaThis) {
try {
viaThis.dispatch(this);
}
catch (Exception e) {
throw new MessageDeliveryFailure("unable to deliver message: " + e.getMessage(), e);
}
}
/**
* Returns the Message instance that this message is in response to.
*
* @return The originating message that this Message is in reply to.
*/
public Message getIncomingMessage() {
return message;
}
boolean committed = false;
/**
* Replaces the behaviour of the delegate message's {@code commit()} method
* with the following:
*
* <ul>
* <li>Sets the {@code toSubject} to the wrapped message's {@code replyTo}
* unless the wrapped message already has a {@code toSubject}.
* <li>Sets the {@code Session} for this message to the same session as the
* incoming message.
* </ul>
*
* @throws RuntimeException
* if the originating message does not have a Session resource, or
* if this message has no {@code toSubject} and the incoming message
* has no {@code replyTo}.
*/
@Override
public void commit() {
// XXX why is this logic deferred until commit()? why not do these things in the constructor?
if (!hasPart(MessageParts.ToSubject)) {
if (message.hasPart(MessageParts.ReplyTo)) {
toSubject(message.get(String.class, MessageParts.ReplyTo));
}
else {
throw new RuntimeException("cannot have a conversation. the incoming message does not specify a recipient ReplyTo subject and you have not specified one.");
}
}
if (message.hasResource("Session")) {
newMessage.copyResource("Session", message);
}
else {
throw new RuntimeException("cannot have a conversation. the incoming message has no session data associated with it.");
}
committed = true;
}
@Override
public boolean isCommited() {
return committed;
}
}