001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.activemq.command; 019 020import java.io.DataInputStream; 021import java.io.DataOutputStream; 022import java.io.IOException; 023import java.io.InputStream; 024import java.io.ObjectOutputStream; 025import java.io.OutputStream; 026import java.io.Serializable; 027import java.util.Arrays; 028import java.util.List; 029import java.util.zip.DeflaterOutputStream; 030import java.util.zip.InflaterInputStream; 031 032import javax.jms.JMSException; 033import javax.jms.ObjectMessage; 034 035import org.apache.activemq.ActiveMQConnection; 036import org.apache.activemq.util.ByteArrayInputStream; 037import org.apache.activemq.util.ByteArrayOutputStream; 038import org.apache.activemq.util.ByteSequence; 039import org.apache.activemq.util.ClassLoadingAwareObjectInputStream; 040import org.apache.activemq.util.JMSExceptionSupport; 041import org.apache.activemq.wireformat.WireFormat; 042 043/** 044 * An <CODE>ObjectMessage</CODE> object is used to send a message that 045 * contains a serializable object in the Java programming language ("Java 046 * object"). It inherits from the <CODE>Message</CODE> interface and adds a 047 * body containing a single reference to an object. Only 048 * <CODE>Serializable</CODE> Java objects can be used. <p/> 049 * <P> 050 * If a collection of Java objects must be sent, one of the 051 * <CODE>Collection</CODE> classes provided since JDK 1.2 can be used. <p/> 052 * <P> 053 * When a client receives an <CODE>ObjectMessage</CODE>, it is in read-only 054 * mode. If a client attempts to write to the message at this point, a 055 * <CODE>MessageNotWriteableException</CODE> is thrown. If 056 * <CODE>clearBody</CODE> is called, the message can now be both read from and 057 * written to. 058 * 059 * @openwire:marshaller code="26" 060 * @see javax.jms.Session#createObjectMessage() 061 * @see javax.jms.Session#createObjectMessage(Serializable) 062 * @see javax.jms.BytesMessage 063 * @see javax.jms.MapMessage 064 * @see javax.jms.Message 065 * @see javax.jms.StreamMessage 066 * @see javax.jms.TextMessage 067 */ 068public class ActiveMQObjectMessage extends ActiveMQMessage implements ObjectMessage, TransientInitializer { 069 070 public static final byte DATA_STRUCTURE_TYPE = CommandTypes.ACTIVEMQ_OBJECT_MESSAGE; 071 072 private transient List<String> trustedPackages = Arrays.asList(ClassLoadingAwareObjectInputStream.serializablePackages); 073 private transient boolean trustAllPackages = false; 074 075 protected transient Serializable object; 076 077 @Override 078 public Message copy() { 079 ActiveMQObjectMessage copy = new ActiveMQObjectMessage(); 080 copy(copy); 081 copy.setTrustAllPackages(trustAllPackages); 082 copy.setTrustedPackages(trustedPackages); 083 return copy; 084 } 085 086 private void copy(ActiveMQObjectMessage copy) { 087 ActiveMQConnection connection = getConnection(); 088 if (connection == null || !connection.isObjectMessageSerializationDefered()) { 089 storeContent(); 090 copy.object = null; 091 } else { 092 copy.object = object; 093 } 094 super.copy(copy); 095 096 } 097 098 @Override 099 public void storeContentAndClear() { 100 storeContent(); 101 object = null; 102 } 103 104 @Override 105 public void storeContent() { 106 ByteSequence bodyAsBytes = getContent(); 107 if (bodyAsBytes == null && object != null) { 108 try { 109 ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); 110 OutputStream os = bytesOut; 111 ActiveMQConnection connection = getConnection(); 112 if (connection != null && connection.isUseCompression()) { 113 compressed = true; 114 os = new DeflaterOutputStream(os); 115 } 116 DataOutputStream dataOut = new DataOutputStream(os); 117 ObjectOutputStream objOut = new ObjectOutputStream(dataOut); 118 objOut.writeObject(object); 119 objOut.flush(); 120 objOut.reset(); 121 objOut.close(); 122 setContent(bytesOut.toByteSequence()); 123 } catch (IOException ioe) { 124 throw new RuntimeException(ioe.getMessage(), ioe); 125 } 126 } 127 } 128 129 @Override 130 public boolean isContentMarshalled() { 131 return content != null || object == null; 132 } 133 134 @Override 135 public byte getDataStructureType() { 136 return DATA_STRUCTURE_TYPE; 137 } 138 139 @Override 140 public String getJMSXMimeType() { 141 return "jms/object-message"; 142 } 143 144 /** 145 * Clears out the message body. Clearing a message's body does not clear its 146 * header values or property entries. <p/> 147 * <P> 148 * If this message body was read-only, calling this method leaves the 149 * message body in the same state as an empty body in a newly created 150 * message. 151 * 152 * @throws JMSException if the JMS provider fails to clear the message body 153 * due to some internal error. 154 */ 155 156 @Override 157 public void clearBody() throws JMSException { 158 super.clearBody(); 159 this.object = null; 160 } 161 162 /** 163 * Sets the serializable object containing this message's data. It is 164 * important to note that an <CODE>ObjectMessage</CODE> contains a 165 * snapshot of the object at the time <CODE>setObject()</CODE> is called; 166 * subsequent modifications of the object will have no effect on the 167 * <CODE>ObjectMessage</CODE> body. 168 * 169 * @param newObject the message's data 170 * @throws JMSException if the JMS provider fails to set the object due to 171 * some internal error. 172 * @throws javax.jms.MessageFormatException if object serialization fails. 173 * @throws javax.jms.MessageNotWriteableException if the message is in 174 * read-only mode. 175 */ 176 177 @Override 178 public void setObject(Serializable newObject) throws JMSException { 179 checkReadOnlyBody(); 180 this.object = newObject; 181 setContent(null); 182 ActiveMQConnection connection = getConnection(); 183 if (connection == null || !connection.isObjectMessageSerializationDefered()) { 184 storeContent(); 185 } 186 } 187 188 /** 189 * Gets the serializable object containing this message's data. The default 190 * value is null. 191 * 192 * @return the serializable object containing this message's data 193 * @throws JMSException 194 */ 195 @Override 196 public Serializable getObject() throws JMSException { 197 if (object == null && getContent() != null) { 198 try { 199 ByteSequence content = getContent(); 200 InputStream is = new ByteArrayInputStream(content); 201 if (isCompressed()) { 202 is = new InflaterInputStream(is); 203 } 204 DataInputStream dataIn = new DataInputStream(is); 205 ClassLoadingAwareObjectInputStream objIn = new ClassLoadingAwareObjectInputStream(dataIn); 206 objIn.setTrustedPackages(trustedPackages); 207 objIn.setTrustAllPackages(trustAllPackages); 208 try { 209 object = (Serializable)objIn.readObject(); 210 } catch (ClassNotFoundException ce) { 211 throw JMSExceptionSupport.create("Failed to build body from content. Serializable class not available to broker. Reason: " + ce, ce); 212 } finally { 213 dataIn.close(); 214 } 215 } catch (IOException e) { 216 throw JMSExceptionSupport.create("Failed to build body from bytes. Reason: " + e, e); 217 } 218 } 219 return this.object; 220 } 221 222 @Override 223 public void beforeMarshall(WireFormat wireFormat) throws IOException { 224 super.beforeMarshall(wireFormat); 225 // may have initiated on vm transport with deferred marshalling 226 storeContent(); 227 } 228 229 @Override 230 public void clearUnMarshalledState() throws JMSException { 231 super.clearUnMarshalledState(); 232 this.object = null; 233 } 234 235 @Override 236 public void onMessageRolledBack() { 237 super.onMessageRolledBack(); 238 239 // lets force the object to be deserialized again - as we could have 240 // changed the object 241 object = null; 242 } 243 244 @Override 245 public void compress() throws IOException { 246 storeContent(); 247 super.compress(); 248 } 249 250 @Override 251 public String toString() { 252 try { 253 getObject(); 254 } catch (JMSException e) { 255 } 256 return super.toString(); 257 } 258 259 public List<String> getTrustedPackages() { 260 return trustedPackages; 261 } 262 263 public void setTrustedPackages(List<String> trustedPackages) { 264 this.trustedPackages = trustedPackages; 265 } 266 267 public boolean isTrustAllPackages() { 268 return trustAllPackages; 269 } 270 271 public void setTrustAllPackages(boolean trustAllPackages) { 272 this.trustAllPackages = trustAllPackages; 273 } 274 275 @Override 276 public void initTransients() { 277 trustedPackages = Arrays.asList(ClassLoadingAwareObjectInputStream.serializablePackages); 278 trustAllPackages = false; 279 } 280}