All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.activemq.command.ActiveMQMapMessage Maven / Gradle / Ivy

There is a newer version: 6.1.4
Show newest version
/**
 * 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 org.apache.activemq.command;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectStreamException;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.InflaterInputStream;

import jakarta.jms.JMSException;
import jakarta.jms.MapMessage;
import jakarta.jms.MessageFormatException;
import jakarta.jms.MessageNotWriteableException;

import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.util.ByteArrayInputStream;
import org.apache.activemq.util.ByteArrayOutputStream;
import org.apache.activemq.util.ByteSequence;
import org.apache.activemq.util.JMSExceptionSupport;
import org.apache.activemq.util.MarshallingSupport;
import org.apache.activemq.wireformat.WireFormat;
import org.fusesource.hawtbuf.UTF8Buffer;

/**
 * A MapMessage object is used to send a set of name-value pairs.
 * The names are String objects, and the values are primitive
 * data types in the Java programming language. The names must have a value that
 * is not null, and not an empty string. The entries can be accessed
 * sequentially or randomly by name. The order of the entries is undefined.
 * MapMessage inherits from the Message interface
 * and adds a message body that contains a Map.
 * 

* The primitive types can be read or written explicitly using methods for each * type. They may also be read or written generically as objects. For instance, * a call to MapMessage.setInt("foo", 6) is equivalent to * MapMessage.setObject("foo", new Integer(6)). Both forms are * provided, because the explicit form is convenient for static programming, and * the object form is needed when types are not known at compile time. *

* When a client receives a MapMessage, it is in read-only mode. * If a client attempts to write to the message at this point, a * MessageNotWriteableException is thrown. If * clearBody is called, the message can now be both read from and * written to. *

* MapMessage objects support the following conversion table. The * marked cases must be supported. The unmarked cases must throw a * JMSException. The String -to-primitive * conversions may throw a runtime exception if the primitive's * valueOf() method does not accept it as a valid * String representation of the primitive. *

* A value written as the row type can be read as the column type.

* *

 * | | boolean byte short char int long float double String byte[] |----------------------------------------------------------------------
 * |boolean | X X |byte | X X X X X |short | X X X X |char | X X |int | X X X |long | X X |float | X X X |double | X X
 * |String | X X X X X X X X |byte[] | X |----------------------------------------------------------------------
 * <p/>
 * 
* *

*

* Attempting to read a null value as a primitive type must be treated as * calling the primitive's corresponding valueOf(String) * conversion method with a null value. Since char does not * support a String conversion, attempting to read a null value * as a char must throw a NullPointerException. * * @openwire:marshaller code="25" * @see jakarta.jms.Session#createMapMessage() * @see jakarta.jms.BytesMessage * @see jakarta.jms.Message * @see jakarta.jms.ObjectMessage * @see jakarta.jms.StreamMessage * @see jakarta.jms.TextMessage */ public class ActiveMQMapMessage extends ActiveMQMessage implements MapMessage { public static final byte DATA_STRUCTURE_TYPE = CommandTypes.ACTIVEMQ_MAP_MESSAGE; protected transient Map map = new HashMap(); @Override protected Object readResolve() throws ObjectStreamException { super.readResolve(); if (this.map == null) { this.map = new HashMap(); } return this; } @Override public Message copy() { ActiveMQMapMessage copy = new ActiveMQMapMessage(); copy(copy); return copy; } private void copy(ActiveMQMapMessage copy) { storeContent(); super.copy(copy); } // We only need to marshal the content if we are hitting the wire. @Override public void beforeMarshall(WireFormat wireFormat) throws IOException { super.beforeMarshall(wireFormat); storeContent(); } @Override public void clearUnMarshalledState() throws JMSException { super.clearUnMarshalledState(); map.clear(); } @Override public void storeContentAndClear() { storeContent(); map.clear(); } @Override public void storeContent() { try { if (getContent() == null && !map.isEmpty()) { ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); OutputStream os = bytesOut; ActiveMQConnection connection = getConnection(); if (connection != null && connection.isUseCompression()) { compressed = true; os = new DeflaterOutputStream(os); } DataOutputStream dataOut = new DataOutputStream(os); MarshallingSupport.marshalPrimitiveMap(map, dataOut); dataOut.close(); setContent(bytesOut.toByteSequence()); } } catch (IOException e) { throw new RuntimeException(e); } } @Override public boolean isContentMarshalled() { return content != null || map == null || map.isEmpty(); } /** * Builds the message body from data * * @throws JMSException * @throws IOException */ private void loadContent() throws JMSException { if (getContent() != null && map.isEmpty()) { map = deserialize(getContent()); } } private Map deserialize(ByteSequence content) throws JMSException { final Map map; try { if (content != null) { InputStream is = new ByteArrayInputStream(content); if (isCompressed()) { is = new InflaterInputStream(is); } DataInputStream dataIn = new DataInputStream(is); map = MarshallingSupport.unmarshalPrimitiveMap(dataIn); dataIn.close(); } else { map = new HashMap<>(); } } catch (IOException e) { throw JMSExceptionSupport.create(e); } return map; } @Override public byte getDataStructureType() { return DATA_STRUCTURE_TYPE; } @Override public String getJMSXMimeType() { return "jms/map-message"; } /** * Clears out the message body. Clearing a message's body does not clear its * header values or property entries. *

* If this message body was read-only, calling this method leaves the * message body in the same state as an empty body in a newly created * message. */ @Override public void clearBody() throws JMSException { super.clearBody(); map.clear(); } /** * Returns the boolean value with the specified name. * * @param name the name of the boolean * @return the boolean value with the specified name * @throws JMSException if the JMS provider fails to read the message due to * some internal error. * @throws MessageFormatException if this type conversion is invalid. */ @Override public boolean getBoolean(String name) throws JMSException { initializeReading(); Object value = map.get(name); if (value == null) { return false; } if (value instanceof Boolean) { return (Boolean) value; } if (value instanceof UTF8Buffer) { return Boolean.valueOf(value.toString()); } if (value instanceof String) { return Boolean.valueOf(value.toString()); } else { throw new MessageFormatException(" cannot read a boolean from " + value.getClass().getName()); } } /** * Returns the byte value with the specified name. * * @param name the name of the byte * @return the byte value with the specified name * @throws JMSException if the JMS provider fails to read the message due to * some internal error. * @throws MessageFormatException if this type conversion is invalid. */ @Override public byte getByte(String name) throws JMSException { initializeReading(); Object value = map.get(name); if (value == null) { return 0; } if (value instanceof Byte) { return (Byte) value; } if (value instanceof UTF8Buffer) { return Byte.valueOf(value.toString()); } if (value instanceof String) { return Byte.valueOf(value.toString()); } else { throw new MessageFormatException(" cannot read a byte from " + value.getClass().getName()); } } /** * Returns the short value with the specified name. * * @param name the name of the short * @return the short value with the specified name * @throws JMSException if the JMS provider fails to read the message due to * some internal error. * @throws MessageFormatException if this type conversion is invalid. */ @Override public short getShort(String name) throws JMSException { initializeReading(); Object value = map.get(name); if (value == null) { return 0; } if (value instanceof Short) { return (Short) value; } if (value instanceof Byte) { return ((Byte)value).shortValue(); } if (value instanceof UTF8Buffer) { return Short.valueOf(value.toString()); } if (value instanceof String) { return Short.valueOf(value.toString()); } else { throw new MessageFormatException(" cannot read a short from " + value.getClass().getName()); } } /** * Returns the Unicode character value with the specified name. * * @param name the name of the Unicode character * @return the Unicode character value with the specified name * @throws JMSException if the JMS provider fails to read the message due to * some internal error. * @throws MessageFormatException if this type conversion is invalid. */ @Override public char getChar(String name) throws JMSException { initializeReading(); Object value = map.get(name); if (value == null) { throw new NullPointerException(); } else if (value instanceof Character) { return (Character) value; } else { throw new MessageFormatException(" cannot read a char from " + value.getClass().getName()); } } /** * Returns the int value with the specified name. * * @param name the name of the int * @return the int value with the specified name * @throws JMSException if the JMS provider fails to read the message due to * some internal error. * @throws MessageFormatException if this type conversion is invalid. */ @Override public int getInt(String name) throws JMSException { initializeReading(); Object value = map.get(name); if (value == null) { return 0; } if (value instanceof Integer) { return (Integer) value; } if (value instanceof Short) { return ((Short)value).intValue(); } if (value instanceof Byte) { return ((Byte)value).intValue(); } if (value instanceof UTF8Buffer) { return Integer.valueOf(value.toString()); } if (value instanceof String) { return Integer.valueOf(value.toString()); } else { throw new MessageFormatException(" cannot read an int from " + value.getClass().getName()); } } /** * Returns the long value with the specified name. * * @param name the name of the long * @return the long value with the specified name * @throws JMSException if the JMS provider fails to read the message due to * some internal error. * @throws MessageFormatException if this type conversion is invalid. */ @Override public long getLong(String name) throws JMSException { initializeReading(); Object value = map.get(name); if (value == null) { return 0; } if (value instanceof Long) { return (Long) value; } if (value instanceof Integer) { return ((Integer)value).longValue(); } if (value instanceof Short) { return ((Short)value).longValue(); } if (value instanceof Byte) { return ((Byte)value).longValue(); } if (value instanceof UTF8Buffer) { return Long.valueOf(value.toString()); } if (value instanceof String) { return Long.valueOf(value.toString()); } else { throw new MessageFormatException(" cannot read a long from " + value.getClass().getName()); } } /** * Returns the float value with the specified name. * * @param name the name of the float * @return the float value with the specified name * @throws JMSException if the JMS provider fails to read the message due to * some internal error. * @throws MessageFormatException if this type conversion is invalid. */ @Override public float getFloat(String name) throws JMSException { initializeReading(); Object value = map.get(name); if (value == null) { return 0; } if (value instanceof Float) { return (Float) value; } if (value instanceof UTF8Buffer) { return Float.valueOf(value.toString()); } if (value instanceof String) { return Float.valueOf(value.toString()); } else { throw new MessageFormatException(" cannot read a float from " + value.getClass().getName()); } } /** * Returns the double value with the specified name. * * @param name the name of the double * @return the double value with the specified name * @throws JMSException if the JMS provider fails to read the message due to * some internal error. * @throws MessageFormatException if this type conversion is invalid. */ @Override public double getDouble(String name) throws JMSException { initializeReading(); Object value = map.get(name); if (value == null) { return 0; } else if (value instanceof Double) { return (Double) value; } else if (value instanceof Float) { return (Float) value; } else if (value instanceof UTF8Buffer) { return Double.valueOf(value.toString()); } else if (value instanceof String) { return Double.valueOf(value.toString()); } else { throw new MessageFormatException("Cannot read a double from " + value.getClass().getName()); } } /** * Returns the String value with the specified name. * * @param name the name of the String * @return the String value with the specified name; if there * is no item by this name, a null value is returned * @throws JMSException if the JMS provider fails to read the message due to * some internal error. * @throws MessageFormatException if this type conversion is invalid. */ @Override public String getString(String name) throws JMSException { initializeReading(); Object value = map.get(name); if (value == null) { return null; } if (value instanceof byte[]) { throw new MessageFormatException("Use getBytes to read a byte array"); } else { return value.toString(); } } /** * Returns the byte array value with the specified name. * * @param name the name of the byte array * @return a copy of the byte array value with the specified name; if there * is no item by this name, a null value is returned. * @throws JMSException if the JMS provider fails to read the message due to * some internal error. * @throws MessageFormatException if this type conversion is invalid. */ @Override public byte[] getBytes(String name) throws JMSException { initializeReading(); Object value = map.get(name); if (value == null) { return null; } if (value instanceof byte[]) { return (byte[])value; } else { throw new MessageFormatException(" cannot read a byte[] from " + value.getClass().getName()); } } /** * Returns the value of the object with the specified name. *

* This method can be used to return, in objectified format, an object in * the Java programming language ("Java object") that had been stored in the * Map with the equivalent setObject method call, or its * equivalent primitive set type method. *

* Note that byte values are returned as byte[], not * Byte[]. * * @param name the name of the Java object * @return a copy of the Java object value with the specified name, in * objectified format (for example, if the object was set as an * int, an Integer is returned); if * there is no item by this name, a null value is returned * @throws JMSException if the JMS provider fails to read the message due to * some internal error. */ @Override public Object getObject(String name) throws JMSException { initializeReading(); Object result = map.get(name); if (result instanceof UTF8Buffer) { result = result.toString(); } return result; } /** * Returns an Enumeration of all the names in the * MapMessage object. * * @return an enumeration of all the names in this MapMessage * @throws JMSException */ @Override public Enumeration getMapNames() throws JMSException { initializeReading(); return Collections.enumeration(map.keySet()); } protected void put(String name, Object value) throws JMSException { if (name == null) { throw new IllegalArgumentException("The name of the property cannot be null."); } if (name.length() == 0) { throw new IllegalArgumentException("The name of the property cannot be an emprty string."); } map.put(name, value); } /** * Sets a boolean value with the specified name into the Map. * * @param name the name of the boolean * @param value the boolean value to set in the Map * @throws JMSException if the JMS provider fails to write the message due * to some internal error. * @throws IllegalArgumentException if the name is null or if the name is an * empty string. * @throws MessageNotWriteableException if the message is in read-only mode. */ @Override public void setBoolean(String name, boolean value) throws JMSException { initializeWriting(); put(name, value ? Boolean.TRUE : Boolean.FALSE); } /** * Sets a byte value with the specified name into the Map. * * @param name the name of the byte * @param value the byte value to set in the Map * @throws JMSException if the JMS provider fails to write the message due * to some internal error. * @throws IllegalArgumentException if the name is null or if the name is an * empty string. * @throws MessageNotWriteableException if the message is in read-only mode. */ @Override public void setByte(String name, byte value) throws JMSException { initializeWriting(); put(name, value); } /** * Sets a short value with the specified name into the Map. * * @param name the name of the short * @param value the short value to set in the Map * @throws JMSException if the JMS provider fails to write the message due * to some internal error. * @throws IllegalArgumentException if the name is null or if the name is an * empty string. * @throws MessageNotWriteableException if the message is in read-only mode. */ @Override public void setShort(String name, short value) throws JMSException { initializeWriting(); put(name, value); } /** * Sets a Unicode character value with the specified name into the Map. * * @param name the name of the Unicode character * @param value the Unicode character value to set in the Map * @throws JMSException if the JMS provider fails to write the message due * to some internal error. * @throws IllegalArgumentException if the name is null or if the name is an * empty string. * @throws MessageNotWriteableException if the message is in read-only mode. */ @Override public void setChar(String name, char value) throws JMSException { initializeWriting(); put(name, value); } /** * Sets an int value with the specified name into the Map. * * @param name the name of the int * @param value the int value to set in the Map * @throws JMSException if the JMS provider fails to write the message due * to some internal error. * @throws IllegalArgumentException if the name is null or if the name is an * empty string. * @throws MessageNotWriteableException if the message is in read-only mode. */ @Override public void setInt(String name, int value) throws JMSException { initializeWriting(); put(name, value); } /** * Sets a long value with the specified name into the Map. * * @param name the name of the long * @param value the long value to set in the Map * @throws JMSException if the JMS provider fails to write the message due * to some internal error. * @throws IllegalArgumentException if the name is null or if the name is an * empty string. * @throws MessageNotWriteableException if the message is in read-only mode. */ @Override public void setLong(String name, long value) throws JMSException { initializeWriting(); put(name, value); } /** * Sets a float value with the specified name into the Map. * * @param name the name of the float * @param value the float value to set in the Map * @throws JMSException if the JMS provider fails to write the message due * to some internal error. * @throws IllegalArgumentException if the name is null or if the name is an * empty string. * @throws MessageNotWriteableException if the message is in read-only mode. */ @Override public void setFloat(String name, float value) throws JMSException { initializeWriting(); put(name, value); } /** * Sets a double value with the specified name into the Map. * * @param name the name of the double * @param value the double value to set in the Map * @throws JMSException if the JMS provider fails to write the message due * to some internal error. * @throws IllegalArgumentException if the name is null or if the name is an * empty string. * @throws MessageNotWriteableException if the message is in read-only mode. */ @Override public void setDouble(String name, double value) throws JMSException { initializeWriting(); put(name, value); } /** * Sets a String value with the specified name into the Map. * * @param name the name of the String * @param value the String value to set in the Map * @throws JMSException if the JMS provider fails to write the message due * to some internal error. * @throws IllegalArgumentException if the name is null or if the name is an * empty string. * @throws MessageNotWriteableException if the message is in read-only mode. */ @Override public void setString(String name, String value) throws JMSException { initializeWriting(); put(name, value); } /** * Sets a byte array value with the specified name into the Map. * * @param name the name of the byte array * @param value the byte array value to set in the Map; the array is copied * so that the value for name will not be * altered by future modifications * @throws JMSException if the JMS provider fails to write the message due * to some internal error. * @throws NullPointerException if the name is null, or if the name is an * empty string. * @throws MessageNotWriteableException if the message is in read-only mode. */ @Override public void setBytes(String name, byte[] value) throws JMSException { initializeWriting(); if (value != null) { put(name, value); } else { map.remove(name); } } /** * Sets a portion of the byte array value with the specified name into the * Map. * * @param name the name of the byte array * @param value the byte array value to set in the Map * @param offset the initial offset within the byte array * @param length the number of bytes to use * @throws JMSException if the JMS provider fails to write the message due * to some internal error. * @throws IllegalArgumentException if the name is null or if the name is an * empty string. * @throws MessageNotWriteableException if the message is in read-only mode. */ @Override public void setBytes(String name, byte[] value, int offset, int length) throws JMSException { initializeWriting(); byte[] data = new byte[length]; System.arraycopy(value, offset, data, 0, length); put(name, data); } /** * Sets an object value with the specified name into the Map. *

* This method works only for the objectified primitive object types (Integer,Double, * Long  ...), String objects, and byte * arrays. * * @param name the name of the Java object * @param value the Java object value to set in the Map * @throws JMSException if the JMS provider fails to write the message due * to some internal error. * @throws IllegalArgumentException if the name is null or if the name is an * empty string. * @throws MessageFormatException if the object is invalid. * @throws MessageNotWriteableException if the message is in read-only mode. */ @Override public void setObject(String name, Object value) throws JMSException { initializeWriting(); if (value != null) { // byte[] not allowed on properties if (!(value instanceof byte[])) { checkValidObject(value); } put(name, value); } else { put(name, null); } } /** * Indicates whether an item exists in this MapMessage * object. * * @param name the name of the item to test * @return true if the item exists * @throws JMSException if the JMS provider fails to determine if the item * exists due to some internal error. */ @Override public boolean itemExists(String name) throws JMSException { initializeReading(); return map.containsKey(name); } private void initializeReading() throws JMSException { loadContent(); } private void initializeWriting() throws MessageNotWriteableException { checkReadOnlyBody(); setContent(null); } @Override public void compress() throws IOException { storeContent(); super.compress(); } @Override public String toString() { return super.toString() + " ActiveMQMapMessage{ " + "theTable = " + map + " }"; } public Map getContentMap() throws JMSException { initializeReading(); return map; } @Override @SuppressWarnings("unchecked") public boolean isBodyAssignableTo(Class c) throws JMSException { final Map map = getContentMap(); if (map == null || map.isEmpty()) { return true; } return c.isAssignableFrom(java.util.Map.class); } @SuppressWarnings("unchecked") protected T doGetBody(Class asType) throws JMSException { storeContent(); final ByteSequence content = getContent(); final Map map = content != null ? deserialize(content) : null; //This implementation treats an empty map as not having a body so if empty //we should return null as well if (map != null && !map.isEmpty()) { map.replaceAll((k, v) -> v instanceof UTF8Buffer ? v.toString() : v); return (T) map; } else { return null; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy