Package org.jboss.errai.bus.client.api.base

Source Code of org.jboss.errai.bus.client.api.base.JSONMessage

/*
* Copyright 2010 JBoss, a divison Red Hat, Inc
*
* 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 org.jboss.errai.bus.client.api.ErrorCallback;
import org.jboss.errai.bus.client.api.HasEncoded;
import org.jboss.errai.bus.client.api.Message;
import org.jboss.errai.bus.client.api.ResourceProvider;
import org.jboss.errai.bus.client.framework.MessageProvider;
import org.jboss.errai.bus.client.framework.RoutingFlags;
import org.jboss.errai.bus.client.protocols.MessageParts;
import org.jboss.errai.common.client.json.JSONEncoderCli;
import org.jboss.errai.common.client.types.DecodingContext;
import org.jboss.errai.common.client.types.EncodingContext;
import org.jboss.errai.common.client.types.TypeHandlerFactory;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
* JSONMessage extends CommandMessage. It represents a message payload.  It implements a builder (or fluent) API
* which is used for constructing sendable messages.
* It is the core messageing API for ErraiBus, and will be the foremost used class within ErraiBus
* by most users.
* <p/>
* <strong>Example Message:</strong>
* <tt><pre>
* CommandMessage msg = CommandMessage.create()
*                          .toSubject("Foo")
*                          .with("Text", "I like chocolate cake.");
* </pre></tt>
* You can transmit a message using the the <tt>sendNowWith()</tt> method by providing an instance of
* {@link org.jboss.errai.bus.client.framework.MessageBus}.
* <p/>
* Messages can be contructed using user-defined standard protocols through the use of enumerations. Both
* <tt>commandType</tt> and message parts can be defined through the use of enumerations.  This helps create
* strongly-defined protocols for communicating with services.  For instance:
* <tt><pre>
* public enum LoginParts {
*    Username, Password
* }
* </pre></tt>
* .. and ..
* <tt><pre>
* public enum LoginCommands {
*    Login, Logout
* }
* </pre></tt>
* These enumerations can than be directly used to build messages and decode incoming messages by a service. For example:
* <tt><pre>
*  CommandMessage.create()
*      .command(LoginCommands.Login)
*      .with(LoginParts.Username, "foo")
*      .with(LoginParts.Password, "bar )
*      .noErrorHandling()
*      .sendNowWith(busInstance);
* </pre></tt>
* Messages may contain serialized objects provided they meet the following criteria:
* <ol>
* <li>The class is annotated with {@link org.jboss.errai.bus.server.annotations.ExposeEntity}</li>
* <li>The class contains a default, no-argument constructor.
* </ol>
*
* @see ConversationMessage
*/
public class JSONMessage extends CommandMessage implements HasEncoded {
  /* String representation of this message and all it's parts */
  protected StringBuffer buf = new StringBuffer();

  /* First is true if the <tt>buf</tt> is empty */
  protected volatile boolean first = true;
  protected volatile boolean ended = false;

  protected final EncodingContext encodingContext = new EncodingContext();


  public static final MessageProvider PROVIDER = new MessageProvider() {
    public Message get() {
      return create();
    }
  };

  /**
   * Create a new JSONMessage.
   *
   * @return a new instance of JSONMessage
   */

  static JSONMessage create() {
    return new JSONMessage();
  }

  protected JSONMessage() {
    _start();
  }

  /**
   * Return the specified command type.  Returns <tt>null</tt> if not specified.
   *
   * @return - String representing the command type.
   */
  public String getCommandType() {
    return (String) parts.get(MessageParts.CommandType.name());
  }

  /**
   * Return the specified message subject.
   *
   * @return
   */
  public String getSubject() {
    return String.valueOf(parts.get(MessageParts.ToSubject.name()));
  }

  /**
   * Set the subject which is the intended recipient of the message.
   *
   * @param subject - subject name.
   * @return -
   */
  public Message toSubject(String subject) {
    if (parts.containsKey(MessageParts.ToSubject.name()))
      throw new IllegalArgumentException("cannot set subject more than once.");


    _addStringPart(MessageParts.ToSubject.name(), subject);
    parts.put(MessageParts.ToSubject.name(), subject);
    return this;
  }

  /**
   * Set the optional command type.
   *
   * @param type
   * @return
   */
  public Message command(Enum type) {
    if (parts.containsKey(MessageParts.CommandType.name()))
      throw new IllegalArgumentException("cannot set command type more than once.");

    _addStringPart(MessageParts.CommandType.name(), type.name());
    parts.put(MessageParts.CommandType.name(), type.name());
    return this;
  }

  /**
   * Set the optional command type.
   *
   * @param type
   * @return
   */
  public Message command(String type) {
    if (parts.containsKey(MessageParts.CommandType.name()))
      throw new IllegalArgumentException("cannot set command type more than once.");


    _addStringPart(MessageParts.CommandType.name(), type);
    parts.put(MessageParts.CommandType.name(), type);
    return this;
  }

  /**
   * Set a message part to the specified value.
   *
   * @param part  - Mesage part
   * @param value - Value instance
   * @return -
   */
  public Message set(Enum part, Object value) {
    return set(part.name(), value);
  }

  /**
   * Set a message part to the specified value.
   *
   * @param part  - Mesage part
   * @param value - Value instance
   * @return -
   */
  public Message set(String part, Object value) {
    if (parts.containsKey(part))
      throw new IllegalArgumentException("cannot set a part more than once: " + part);

    _addObjectPart(part, value);
    parts.put(part, value);

    return this;
  }

  /**
   * Removes a message part
   *
   * @param part - Message part
   */
  public void remove(String part) {
    throw new UnsupportedOperationException();
    //  parts.remove(part);
  }

  /**
   * Removes a message part
   *
   * @param part - Message part
   */
  public void remove(Enum part) {
    throw new UnsupportedOperationException();
  }

  /**
   * Copy the same value from the specified message into this message.
   *
   * @param part    - Part to copy
   * @param message - CommandMessage to copy from
   * @return -
   */
  public Message copy(Enum part, Message message) {
    set(part, message.get(Object.class, part));
    return this;
  }

  /**
   * Copy the same value from the specified message into this message.
   *
   * @param part    - Part to copy
   * @param message - CommandMessage to copy from
   * @return -
   */
  public Message copy(String part, Message message) {
    set(part, message.get(Object.class, part));
    return this;
  }

  /**
   * Set routing flags for message.
   *
   * @param flag - Routing flag to set
   */
  public void setFlag(RoutingFlags flag) {
    routingFlags |= flag.flag();
  }

  /**
   * Unset routing flags for message.
   *
   * @param flag - Routing flag to unset
   */
  public void unsetFlag(RoutingFlags flag) {
    if ((routingFlags & flag.flag()) != 0) {
      routingFlags ^= flag.flag();
    }
  }

  /**
   * Checks if specified routing flag has been set.
   *
   * @param flag - flag to check for
   * @return true if flag has been set.
   */
  public boolean isFlagSet(RoutingFlags flag) {
    return (routingFlags & flag.flag()) != 0;
  }


  /**
   * Get the specified message part in the specified type.  A <tt>ClassCastException</tt> is thrown if the value
   * cannot be coerced to the specified type.
   *
   * @param type - Type to be returned.
   * @param part - Message part.
   * @param <T>  - Type to be returned.
   * @return - Value in the specified type.
   */
  @SuppressWarnings({"UnusedDeclaration"})
  public <T> T get(Class<T> type, Enum<?> part) {
    //noinspection unchecked
    Object value = parts.get(part.toString());
    return value == null ? null : TypeHandlerFactory.convert(value.getClass(), type, value, new DecodingContext());
  }

  /**
   * Get the specified message part in the specified type.  A <tt>ClassCastException</tt> is thrown if the value
   * cannot be coerced to the specified type.
   *
   * @param type - Type to be returned.
   * @param part - Message part.
   * @param <T>  - Type to be returned.
   * @return - Value in the specified type.
   */
  @SuppressWarnings({"UnusedDeclaration"})
  public <T> T get(Class<T> type, String part) {
    //noinspection unchecked
    Object value = parts.get(part);
    return value == null ? null : TypeHandlerFactory.convert(value.getClass(), type, value, new DecodingContext());
  }

  /**
   * Returns true if the specified part is defined in the message.
   *
   * @param part - Message part.
   * @return - boolean value indiciating whether or not specified part is present in the message.
   */
  public boolean hasPart(Enum part) {
    return hasPart(part.name());
  }

  /**
   * Returns true if the specified part is defined in the message.
   *
   * @param part - Message part.
   * @return - boolean value indiciating whether or not specified part is present in the message.
   */
  public boolean hasPart(String part) {
    return parts.containsKey(part);
  }

  /**
   * Return a Map of all the specified parts.
   *
   * @return - An unmodifiable Map of parts.
   */
  public Map<String, Object> getParts() {
    return Collections.unmodifiableMap(parts);
  }

  /**
   * Set the message to contain the specified parts.  Note: This overwrites any existing message contents.
   *
   * @param parts - Parts to be used in the message.
   * @return -
   */
  public Message setParts(Map<String, Object> parts) {
    throw new UnsupportedOperationException();
  }

  /**
   * Add the specified parts to the message.
   *
   * @param parts - Parts to be added to the message.
   * @return -
   */
  public Message addAllParts(Map<String, Object> parts) {
    for (Map.Entry<String, Object> entry : parts.entrySet()) {
      set(entry.getKey(), entry.getValue());
    }
    return this;
  }

  public Message addAllProvidedParts(Map<String, ResourceProvider> parts) {
    for (Map.Entry<String, ResourceProvider> entry : parts.entrySet()) {
      setProvidedPart(entry.getKey(), entry.getValue());
    }
    return this;
  }

  /**
   * Set a transient resource.  A resource is not transmitted beyond the current bus scope.  It can be used for
   * managing the lifecycle of a message within a bus.
   *
   * @param key - Name of resource
   * @param res - Instance of resouce
   * @return -
   */
  public Message setResource(String key, Object res) {
    if (this.resources == null) this.resources = new HashMap<String, Object>();
    this.resources.put(key, res);
    return this;
  }

  /**
   * Obtain a transient resource based on the specified key.
   *
   * @param key - Name of resource.
   * @return - Instancee of resource.
   */
  public <T> T getResource(Class<T> type, String key) {
    return (T) (this.resources == null ? null : this.resources.get(key));
  }

  /**
   * Copy a transient resource to this mesage from the specified message.
   *
   * @param key      - Name of resource.
   * @param copyFrom - Message to copy from.
   * @return
   */
  public Message copyResource(String key, Message copyFrom) {
    if (!copyFrom.hasResource(key)) {
      throw new RuntimeException("Cannot copy resource '" + key + "': no such resource.");
    }
    setResource(key, copyFrom.getResource(Object.class, key));
    return this;
  }

  /**
   * Registers an error callback function for this message.
   *
   * @param callback - error callback
   * @return this message
   */
  public Message errorsCall(ErrorCallback callback) {
    if (this.errorsCall != null) {
      throw new RuntimeException("An ErrorCallback is already registered");
    }
    this.errorsCall = callback;
    return this;
  }

  /**
   * Gets the error callback set for this message
   *
   * @return the error callback function
   */
  public ErrorCallback getErrorCallback() {
    return errorsCall;
  }

  /**
   * Returns true if the specified transient resource is present.
   *
   * @param key - Name of resouce
   * @return - boolean value indicating if the specified resource is present in the message.
   */
  public boolean hasResource(String key) {
    return this.resources != null && this.resources.containsKey(key);
  }

  /**
   * Add the Map of resources to the message.
   *
   * @param resources - Map of resource
   */
  public void addResources(Map<String, ?> resources) {
    if (this.resources == null) this.resources = new HashMap<String, Object>(resources);
    else {
      this.resources.putAll(resources);
    }
  }

  /**
   * Returns an encoded string representation of this message in the buffer
   *
   * @return an encoded string of the buffer
   */
  public String getEncoded() {
    synchronized (this) {
      if (!ended) _end();
      return buf.toString();
    }
  }

  /**
   * Returns a <tt>String</tt> representation of this message
   *
   * @return a string representation of this message
   */
  @Override
  public String toString() {
    return buf.toString();
  }

  /**
   * Starts appending the beginning of the JSON message to the buffer <tt>buf</tt>
   */
  protected void _start() {
    first = true;
    buf.append("{");
  }

  /**
   * Appends the closing brace to the end of the buffer denoting the end of the message
   */
  protected void _end() {
    synchronized (this) {
      if (!ended) {
        ended = true;
        buf.append("}");
      }
    }
  }

  /**
   * Appends a separator to the buffer where needed
   */
  protected void _sep() {
    if (first) {
      first = false;
    }
    else {
      buf.append(',');
    }
  }

  /**
   * Append strings in JSON notation to the <tt>buf</tt>
   *
   * @param a - the real name of the part
   * @param b - the value of the part
   */
  protected void _addStringPart(String a, String b) {
    _sep();
    buf.append('\"').append(a).append('\"').append(':')
        .append('\"').append(b).append("\"");
  }

  /**
   * Append objects in JSON notation to the <tt>buf</tt>
   *
   * @param a - message part
   * @param b - the value of the message's part
   */
  protected void _addObjectPart(String a, Object b) {
    _sep();
    buf.append('\"').append(a).append('\"').append(':')
        .append(new JSONEncoderCli().encode(b, encodingContext));
  }
}
TOP

Related Classes of org.jboss.errai.bus.client.api.base.JSONMessage

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.