| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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 flex.messaging.messages; |
| |
| import java.io.IOException; |
| import java.io.ObjectInput; |
| import java.io.ObjectOutput; |
| |
| import flex.messaging.log.LogCategories; |
| import flex.messaging.util.UUIDUtils; |
| |
| /** |
| * A message that represents an infrastructure command passed between |
| * client and server. Subscribe/unsubscribe operations result in |
| * CommandMessage transmissions, as do polling operations. |
| */ |
| public class CommandMessage extends AsyncMessage { |
| /** |
| * Log category for <code>CommandMessage</code>. |
| */ |
| public static final String LOG_CATEGORY = LogCategories.MESSAGE_COMMAND; |
| |
| // THESE VALUES MUST BE THE SAME ON CLIENT AND SERVER |
| /** |
| * This operation is used to subscribe to a remote destination. |
| */ |
| public static final int SUBSCRIBE_OPERATION = 0; |
| |
| /** |
| * This operation is used to unsubscribe from a remote destination. |
| */ |
| public static final int UNSUBSCRIBE_OPERATION = 1; |
| |
| /** |
| * This operation is used to poll a remote destination for pending, |
| * undelivered messages. |
| */ |
| public static final int POLL_OPERATION = 2; |
| |
| /** |
| * This operation is used by a remote destination to sync missed or cached messages |
| * back to a client as a result of a client issued poll command. |
| */ |
| public static final int CLIENT_SYNC_OPERATION = 4; |
| |
| /** |
| * This operation is used to test connectivity over the current channel to |
| * the remote endpoint. |
| */ |
| public static final int CLIENT_PING_OPERATION = 5; |
| |
| /** |
| * This operation is used to request a list of failover endpoint URIs |
| * for the remote destination based on cluster membership. |
| */ |
| public static final int CLUSTER_REQUEST_OPERATION = 7; |
| |
| /** |
| * This operation is used to send credentials to the endpoint so that |
| * the user can be logged in over the current channel. |
| * The credentials need to be Base64 encoded and stored in the <code>body</code> |
| * of the message. |
| */ |
| public static final int LOGIN_OPERATION = 8; |
| |
| /** |
| * This operation is used to log the user out of the current channel, and |
| * will invalidate the server session if the channel is HTTP based. |
| */ |
| public static final int LOGOUT_OPERATION = 9; |
| |
| /** |
| * This operation is used to indicate that the client's subscription to a |
| * remote destination has been invalidated. |
| */ |
| public static final int SUBSCRIPTION_INVALIDATE_OPERATION = 10; |
| |
| /** |
| * This operation is used by the MultiTopicConsumer to subscribe/unsubscribe |
| * from multiple subtopics/selectors in the same message. |
| */ |
| public static final int MULTI_SUBSCRIBE_OPERATION = 11; |
| |
| /** |
| * This operation is used to indicate that a channel has disconnected. |
| */ |
| public static final int DISCONNECT_OPERATION = 12; |
| |
| /** |
| * This operation is used to trigger a client connect attempt. |
| */ |
| public static final int TRIGGER_CONNECT_OPERATION = 13; |
| |
| /** |
| * This is the default operation for new CommandMessage instances. |
| */ |
| public static final int UNKNOWN_OPERATION = 10000; |
| |
| /** |
| * Endpoints can imply what features they support by reporting the |
| * latest version of messaging they are capable of during the handshake of |
| * the initial ping CommandMessage. |
| */ |
| public static final String MESSAGING_VERSION = "DSMessagingVersion"; |
| |
| /** |
| * The name for the selector header in subscribe messages. |
| */ |
| public static final String SELECTOR_HEADER = "DSSelector"; |
| |
| /** |
| * The name for the header used internaly on the server to indicate that an unsubscribe |
| * message is due to a client subscription being invalidated. |
| */ |
| public static final String SUBSCRIPTION_INVALIDATED_HEADER = "DSSubscriptionInvalidated"; |
| |
| /** |
| * Durable JMS subscriptions are preserved when an unsubscribe message |
| * has this parameter set to true in its header. |
| */ |
| public static final String PRESERVE_DURABLE_HEADER = "DSPreserveDurable"; |
| |
| /** |
| * Header to indicate that the Channel needs the configuration from the |
| * server. |
| */ |
| public static final String NEEDS_CONFIG_HEADER = "DSNeedsConfig"; |
| |
| /** |
| * Header used in a MULTI_SUBSCRIBE message to specify an Array of subtopic/selector |
| * pairs to add to the existing set of subscriptions. |
| */ |
| public static final String ADD_SUBSCRIPTIONS = "DSAddSub"; |
| |
| /** |
| * Like the above, but specifies the subtopic/selector array of to remove. |
| */ |
| public static final String REMOVE_SUBSCRIPTIONS = "DSRemSub"; |
| |
| /** |
| * The separator used in the add and remove subscription headers for |
| * multi subscribe messages. |
| */ |
| public static final String SUBTOPIC_SEPARATOR = "_;_"; |
| |
| /** |
| * Header to drive an idle wait time before the next client poll request. |
| */ |
| public static final String POLL_WAIT_HEADER = "DSPollWait"; |
| |
| /** |
| * Header to suppress poll response processing. If a client has a long-poll |
| * parked on the server and issues another poll, the response to this subsequent poll |
| * should be tagged with this header in which case the response is treated as a |
| * no-op and the next poll will not be scheduled. Without this, a subsequent poll |
| * will put the channel and endpoint into a busy polling cycle. |
| */ |
| public static final String NO_OP_POLL_HEADER = "DSNoOpPoll"; |
| |
| /** |
| * Internal header used to tag poll messages when a poll-wait must be suppressed. |
| */ |
| public static final String SUPPRESS_POLL_WAIT_HEADER = "DSSuppressPollWait"; |
| |
| /** |
| * Header to specify which character set encoding was used while encoding |
| * login credentials. |
| */ |
| public static final String CREDENTIALS_CHARSET_HEADER = "DSCredentialsCharset"; |
| |
| /** |
| * Header to indicate the maximum number of messages a Consumer wants to |
| * receive per second. |
| */ |
| public static final String MAX_FREQUENCY_HEADER = "DSMaxFrequency"; |
| |
| /** |
| * Header that indicates the message is a heartbeat. |
| */ |
| public static final String HEARTBEAT_HEADER = "DS<3"; |
| |
| /** |
| * Header that indicates the client application has successfully registered for push notifications. |
| */ |
| public static final String PUSH_NOTIFICATION_REGISTERED_HEADER = "DSApplicationRegisteredForPush"; |
| |
| /** |
| * Header that provides additional meta-information when the client has registered for push notifications. |
| */ |
| public static final String PUSH_REGISTRATION_INFORMATION = "DSPushRegisteredInformation"; |
| |
| /** |
| * The position of the operation flag within all flags. |
| * Constant used during serialization. |
| */ |
| private static byte OPERATION_FLAG = 1; |
| |
| /** |
| * This number was generated using the 'serialver' command line tool. |
| * This number should remain consistent with the version used by |
| * ColdFusion to communicate with the message broker over RMI. |
| */ |
| private static final long serialVersionUID = -4026438615587526303L; |
| |
| /** |
| * The operation names that map to each of the operation constants above. |
| * The constants in this list should remain parallel to the above constants |
| */ |
| static final String[] operationNames = { |
| "subscribe", "unsubscribe", "poll", "unused3", "client_sync", "client_ping", |
| "unused6", "cluster_request", "login", "logout", "subscription_invalidate", |
| "multi_subscribe", "disconnect", "trigger_connect", "state_change" |
| }; |
| |
| /** |
| * The operation to execute for messages of this type. |
| */ |
| private int operation = UNKNOWN_OPERATION; |
| |
| /** |
| * Constructs a <code>CommandMessage</code> instance. |
| * The message id is set to a universally unique value, and the |
| * timestamp for the message is set to the current system timestamp. |
| * The operation is set to a default value of <code>UNKNOWN_OPERATION</code>. |
| */ |
| public CommandMessage() { |
| this.messageId = UUIDUtils.createUUID(); |
| this.timestamp = System.currentTimeMillis(); |
| } |
| |
| /** |
| * Constructs a <code>CommandMessage</code> instance. |
| * The message id is set to a universally unique value, and the |
| * timestamp for the message is set to the current system timestamp. |
| * |
| * @param operation The operation for the <code>CommandMessage</code>; one of the operation constants. |
| */ |
| public CommandMessage(int operation) { |
| this(); |
| this.operation = operation; |
| } |
| |
| /** |
| * Returns the operation for this <code>CommandMessage</code>. |
| * |
| * @return The operation for this <code>CommandMessage</code>. |
| */ |
| public int getOperation() { |
| return operation; |
| } |
| |
| /** |
| * Sets the operation for this <code>CommandMessage</code>. |
| * |
| * @param operation The operation for this <code>CommandMessage</code>. |
| */ |
| public void setOperation(int operation) { |
| this.operation = operation; |
| } |
| |
| /** |
| * |
| */ |
| public Message getSmallMessage() { |
| // We shouldn't use small messages for PING or LOGIN operations as the |
| // messaging version handshake would not yet be complete... for now just |
| // optimize POLL operations. |
| if (operation == POLL_OPERATION) { |
| return new CommandMessageExt(this); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Debugging function which returns the name of the operation for |
| * a given operation code. |
| */ |
| public static String operationToString(int operation) { |
| if (operation < 0 || operation >= operationNames.length) |
| return "invalid." + operation; |
| return operationNames[operation]; |
| } |
| |
| /** |
| * |
| */ |
| public void readExternal(ObjectInput input) throws IOException, ClassNotFoundException { |
| super.readExternal(input); |
| |
| short[] flagsArray = readFlags(input); |
| for (int i = 0; i < flagsArray.length; i++) { |
| short flags = flagsArray[i]; |
| short reservedPosition = 0; |
| |
| if (i == 0) { |
| if ((flags & OPERATION_FLAG) != 0) |
| operation = ((Number) input.readObject()).intValue(); |
| |
| reservedPosition = 1; |
| } |
| |
| // For forwards compatibility, read in any other flagged objects |
| // to preserve the integrity of the input stream... |
| if ((flags >> reservedPosition) != 0) { |
| for (short j = reservedPosition; j < 6; j++) { |
| if (((flags >> j) & 1) != 0) { |
| input.readObject(); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Utility method to pretty print a <code>CommandMessage</code>. |
| * |
| * @param indentLevel This method may be invoked recursively so this argument |
| * allows nested messages to print relative to the current print stack. |
| */ |
| protected String toStringFields(int indentLevel) { |
| String sep = getFieldSeparator(indentLevel); |
| String s = sep + "operation = " + operationToString(operation); |
| if (operation == SUBSCRIBE_OPERATION) |
| s += sep + "selector = " + getHeader(SELECTOR_HEADER); |
| if (operation != LOGIN_OPERATION) { |
| s += super.toStringFields(indentLevel); |
| } else { |
| s += sep + "clientId = " + clientId; |
| s += sep + "destination = " + destination; |
| s += sep + "messageId = " + messageId; |
| s += sep + "timestamp = " + timestamp; |
| s += sep + "timeToLive = " + timeToLive; |
| s += sep + "***not printing credentials***"; |
| } |
| return s; |
| } |
| |
| /** |
| * |
| */ |
| public void writeExternal(ObjectOutput output) throws IOException { |
| super.writeExternal(output); |
| |
| short flags = 0; |
| |
| if (operation != 0) |
| flags |= OPERATION_FLAG; |
| |
| output.writeByte(flags); |
| |
| if (operation != 0) |
| output.writeObject(new Integer(operation)); |
| } |
| |
| /** |
| * Utility method to build the log category to use for logging <code>CommandMessage</code>s. |
| */ |
| public String logCategory() { |
| return LOG_CATEGORY + "." + operationToString(operation); |
| } |
| } |