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 */
017package org.apache.activemq.command;
018
019import java.io.IOException;
020import java.io.UnsupportedEncodingException;
021import java.util.Enumeration;
022import java.util.HashMap;
023import java.util.List;
024import java.util.Map;
025import java.util.Vector;
026
027import javax.jms.DeliveryMode;
028import javax.jms.Destination;
029import javax.jms.JMSException;
030import javax.jms.MessageFormatException;
031import javax.jms.MessageNotWriteableException;
032
033import org.apache.activemq.ActiveMQConnection;
034import org.apache.activemq.ScheduledMessage;
035import org.apache.activemq.broker.scheduler.CronParser;
036import org.apache.activemq.filter.PropertyExpression;
037import org.apache.activemq.state.CommandVisitor;
038import org.apache.activemq.util.Callback;
039import org.apache.activemq.util.JMSExceptionSupport;
040import org.apache.activemq.util.TypeConversionSupport;
041import org.fusesource.hawtbuf.UTF8Buffer;
042
043/**
044 *
045 * @openwire:marshaller code="23"
046 */
047public class ActiveMQMessage extends Message implements org.apache.activemq.Message, ScheduledMessage {
048    public static final byte DATA_STRUCTURE_TYPE = CommandTypes.ACTIVEMQ_MESSAGE;
049    public static final String DLQ_DELIVERY_FAILURE_CAUSE_PROPERTY = "dlqDeliveryFailureCause";
050    public static final String BROKER_PATH_PROPERTY = "JMSActiveMQBrokerPath";
051
052    private static final Map<String, PropertySetter> JMS_PROPERTY_SETERS = new HashMap<String, PropertySetter>();
053
054    protected transient Callback acknowledgeCallback;
055
056    @Override
057    public byte getDataStructureType() {
058        return DATA_STRUCTURE_TYPE;
059    }
060
061    @Override
062    public Message copy() {
063        ActiveMQMessage copy = new ActiveMQMessage();
064        copy(copy);
065        return copy;
066    }
067
068    protected void copy(ActiveMQMessage copy) {
069        super.copy(copy);
070        copy.acknowledgeCallback = acknowledgeCallback;
071    }
072
073    @Override
074    public int hashCode() {
075        MessageId id = getMessageId();
076        if (id != null) {
077            return id.hashCode();
078        } else {
079            return super.hashCode();
080        }
081    }
082
083    @Override
084    public boolean equals(Object o) {
085        if (this == o) {
086            return true;
087        }
088        if (o == null || o.getClass() != getClass()) {
089            return false;
090        }
091
092        ActiveMQMessage msg = (ActiveMQMessage) o;
093        MessageId oMsg = msg.getMessageId();
094        MessageId thisMsg = this.getMessageId();
095        return thisMsg != null && oMsg != null && oMsg.equals(thisMsg);
096    }
097
098    @Override
099    public void acknowledge() throws JMSException {
100        if (acknowledgeCallback != null) {
101            try {
102                acknowledgeCallback.execute();
103            } catch (JMSException e) {
104                throw e;
105            } catch (Throwable e) {
106                throw JMSExceptionSupport.create(e);
107            }
108        }
109    }
110
111    @Override
112    public void clearBody() throws JMSException {
113        setContent(null);
114        readOnlyBody = false;
115    }
116
117    @Override
118    public String getJMSMessageID() {
119        MessageId messageId = this.getMessageId();
120        if (messageId == null) {
121            return null;
122        }
123        return messageId.toString();
124    }
125
126    /**
127     * Seems to be invalid because the parameter doesn't initialize MessageId
128     * instance variables ProducerId and ProducerSequenceId
129     *
130     * @param value
131     * @throws JMSException
132     */
133    @Override
134    public void setJMSMessageID(String value) throws JMSException {
135        if (value != null) {
136            try {
137                MessageId id = new MessageId(value);
138                this.setMessageId(id);
139            } catch (NumberFormatException e) {
140                // we must be some foreign JMS provider or strange user-supplied
141                // String
142                // so lets set the IDs to be 1
143                MessageId id = new MessageId();
144                id.setTextView(value);
145                this.setMessageId(id);
146            }
147        } else {
148            this.setMessageId(null);
149        }
150    }
151
152    /**
153     * This will create an object of MessageId. For it to be valid, the instance
154     * variable ProducerId and producerSequenceId must be initialized.
155     *
156     * @param producerId
157     * @param producerSequenceId
158     * @throws JMSException
159     */
160    public void setJMSMessageID(ProducerId producerId, long producerSequenceId) throws JMSException {
161        MessageId id = null;
162        try {
163            id = new MessageId(producerId, producerSequenceId);
164            this.setMessageId(id);
165        } catch (Throwable e) {
166            throw JMSExceptionSupport.create("Invalid message id '" + id + "', reason: " + e.getMessage(), e);
167        }
168    }
169
170    @Override
171    public long getJMSTimestamp() {
172        return this.getTimestamp();
173    }
174
175    @Override
176    public void setJMSTimestamp(long timestamp) {
177        this.setTimestamp(timestamp);
178    }
179
180    @Override
181    public String getJMSCorrelationID() {
182        return this.getCorrelationId();
183    }
184
185    @Override
186    public void setJMSCorrelationID(String correlationId) {
187        this.setCorrelationId(correlationId);
188    }
189
190    @Override
191    public byte[] getJMSCorrelationIDAsBytes() throws JMSException {
192        return encodeString(this.getCorrelationId());
193    }
194
195    @Override
196    public void setJMSCorrelationIDAsBytes(byte[] correlationId) throws JMSException {
197        this.setCorrelationId(decodeString(correlationId));
198    }
199
200    @Override
201    public String getJMSXMimeType() {
202        return "jms/message";
203    }
204
205    protected static String decodeString(byte[] data) throws JMSException {
206        try {
207            if (data == null) {
208                return null;
209            }
210            return new String(data, "UTF-8");
211        } catch (UnsupportedEncodingException e) {
212            throw new JMSException("Invalid UTF-8 encoding: " + e.getMessage());
213        }
214    }
215
216    protected static byte[] encodeString(String data) throws JMSException {
217        try {
218            if (data == null) {
219                return null;
220            }
221            return data.getBytes("UTF-8");
222        } catch (UnsupportedEncodingException e) {
223            throw new JMSException("Invalid UTF-8 encoding: " + e.getMessage());
224        }
225    }
226
227    @Override
228    public Destination getJMSReplyTo() {
229        return this.getReplyTo();
230    }
231
232    @Override
233    public void setJMSReplyTo(Destination destination) throws JMSException {
234        this.setReplyTo(ActiveMQDestination.transform(destination));
235    }
236
237    @Override
238    public Destination getJMSDestination() {
239        return this.getDestination();
240    }
241
242    @Override
243    public void setJMSDestination(Destination destination) throws JMSException {
244        this.setDestination(ActiveMQDestination.transform(destination));
245    }
246
247    @Override
248    public int getJMSDeliveryMode() {
249        return this.isPersistent() ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT;
250    }
251
252    @Override
253    public void setJMSDeliveryMode(int mode) {
254        this.setPersistent(mode == DeliveryMode.PERSISTENT);
255    }
256
257    @Override
258    public boolean getJMSRedelivered() {
259        return this.isRedelivered();
260    }
261
262    @Override
263    public void setJMSRedelivered(boolean redelivered) {
264        this.setRedelivered(redelivered);
265    }
266
267    @Override
268    public String getJMSType() {
269        return this.getType();
270    }
271
272    @Override
273    public void setJMSType(String type) {
274        this.setType(type);
275    }
276
277    @Override
278    public long getJMSExpiration() {
279        return this.getExpiration();
280    }
281
282    @Override
283    public void setJMSExpiration(long expiration) {
284        this.setExpiration(expiration);
285    }
286
287    @Override
288    public int getJMSPriority() {
289        return this.getPriority();
290    }
291
292    @Override
293    public void setJMSPriority(int priority) {
294        this.setPriority((byte) priority);
295    }
296
297    @Override
298    public void clearProperties() {
299        super.clearProperties();
300        readOnlyProperties = false;
301    }
302
303    @Override
304    public boolean propertyExists(String name) throws JMSException {
305        try {
306            return (this.getProperties().containsKey(name) || getObjectProperty(name)!= null);
307        } catch (IOException e) {
308            throw JMSExceptionSupport.create(e);
309        }
310    }
311
312    @Override
313    @SuppressWarnings("rawtypes")
314    public Enumeration getPropertyNames() throws JMSException {
315        try {
316            Vector<String> result = new Vector<String>(this.getProperties().keySet());
317            if( getRedeliveryCounter()!=0 ) {
318                result.add("JMSXDeliveryCount");
319            }
320            if( getGroupID()!=null ) {
321                result.add("JMSXGroupID");
322            }
323            if( getGroupID()!=null ) {
324                result.add("JMSXGroupSeq");
325            }
326            if( getUserID()!=null ) {
327                result.add("JMSXUserID");
328            }
329            return result.elements();
330        } catch (IOException e) {
331            throw JMSExceptionSupport.create(e);
332        }
333    }
334
335    /**
336     * return all property names, including standard JMS properties and JMSX properties
337     * @return  Enumeration of all property names on this message
338     * @throws JMSException
339     */
340    @SuppressWarnings("rawtypes")
341    public Enumeration getAllPropertyNames() throws JMSException {
342        try {
343            Vector<String> result = new Vector<String>(this.getProperties().keySet());
344            result.addAll(JMS_PROPERTY_SETERS.keySet());
345            return result.elements();
346        } catch (IOException e) {
347            throw JMSExceptionSupport.create(e);
348        }
349    }
350
351    interface PropertySetter {
352        void set(Message message, Object value) throws MessageFormatException;
353    }
354
355    static {
356        JMS_PROPERTY_SETERS.put("JMSXDeliveryCount", new PropertySetter() {
357            @Override
358            public void set(Message message, Object value) throws MessageFormatException {
359                Integer rc = (Integer) TypeConversionSupport.convert(value, Integer.class);
360                if (rc == null) {
361                    throw new MessageFormatException("Property JMSXDeliveryCount cannot be set from a " + value.getClass().getName() + ".");
362                }
363                message.setRedeliveryCounter(rc.intValue() - 1);
364            }
365        });
366        JMS_PROPERTY_SETERS.put("JMSXGroupID", new PropertySetter() {
367            @Override
368            public void set(Message message, Object value) throws MessageFormatException {
369                String rc = (String) TypeConversionSupport.convert(value, String.class);
370                if (rc == null) {
371                    throw new MessageFormatException("Property JMSXGroupID cannot be set from a " + value.getClass().getName() + ".");
372                }
373                message.setGroupID(rc);
374            }
375        });
376        JMS_PROPERTY_SETERS.put("JMSXGroupSeq", new PropertySetter() {
377            @Override
378            public void set(Message message, Object value) throws MessageFormatException {
379                Integer rc = (Integer) TypeConversionSupport.convert(value, Integer.class);
380                if (rc == null) {
381                    throw new MessageFormatException("Property JMSXGroupSeq cannot be set from a " + value.getClass().getName() + ".");
382                }
383                message.setGroupSequence(rc.intValue());
384            }
385        });
386        JMS_PROPERTY_SETERS.put("JMSCorrelationID", new PropertySetter() {
387            @Override
388            public void set(Message message, Object value) throws MessageFormatException {
389                String rc = (String) TypeConversionSupport.convert(value, String.class);
390                if (rc == null) {
391                    throw new MessageFormatException("Property JMSCorrelationID cannot be set from a " + value.getClass().getName() + ".");
392                }
393                ((ActiveMQMessage) message).setJMSCorrelationID(rc);
394            }
395        });
396        JMS_PROPERTY_SETERS.put("JMSDeliveryMode", new PropertySetter() {
397            @Override
398            public void set(Message message, Object value) throws MessageFormatException {
399                Integer rc = null;
400                try {
401                    rc = (Integer) TypeConversionSupport.convert(value, Integer.class);
402                } catch (NumberFormatException nfe) {
403                    if (value instanceof String) {
404                        if (((String) value).equalsIgnoreCase("PERSISTENT")) {
405                            rc = DeliveryMode.PERSISTENT;
406                        } else if (((String) value).equalsIgnoreCase("NON_PERSISTENT")) {
407                            rc = DeliveryMode.NON_PERSISTENT;
408                        } else {
409                            throw nfe;
410                        }
411                    }
412                }
413                if (rc == null) {
414                    Boolean bool = (Boolean) TypeConversionSupport.convert(value, Boolean.class);
415                    if (bool == null) {
416                        throw new MessageFormatException("Property JMSDeliveryMode cannot be set from a " + value.getClass().getName() + ".");
417                    } else {
418                        rc = bool.booleanValue() ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT;
419                    }
420                }
421                ((ActiveMQMessage) message).setJMSDeliveryMode(rc);
422            }
423        });
424        JMS_PROPERTY_SETERS.put("JMSExpiration", new PropertySetter() {
425            @Override
426            public void set(Message message, Object value) throws MessageFormatException {
427                Long rc = (Long) TypeConversionSupport.convert(value, Long.class);
428                if (rc == null) {
429                    throw new MessageFormatException("Property JMSExpiration cannot be set from a " + value.getClass().getName() + ".");
430                }
431                ((ActiveMQMessage) message).setJMSExpiration(rc.longValue());
432            }
433        });
434        JMS_PROPERTY_SETERS.put("JMSPriority", new PropertySetter() {
435            @Override
436            public void set(Message message, Object value) throws MessageFormatException {
437                Integer rc = (Integer) TypeConversionSupport.convert(value, Integer.class);
438                if (rc == null) {
439                    throw new MessageFormatException("Property JMSPriority cannot be set from a " + value.getClass().getName() + ".");
440                }
441                ((ActiveMQMessage) message).setJMSPriority(rc.intValue());
442            }
443        });
444        JMS_PROPERTY_SETERS.put("JMSRedelivered", new PropertySetter() {
445            @Override
446            public void set(Message message, Object value) throws MessageFormatException {
447                Boolean rc = (Boolean) TypeConversionSupport.convert(value, Boolean.class);
448                if (rc == null) {
449                    throw new MessageFormatException("Property JMSRedelivered cannot be set from a " + value.getClass().getName() + ".");
450                }
451                ((ActiveMQMessage) message).setJMSRedelivered(rc.booleanValue());
452            }
453        });
454        JMS_PROPERTY_SETERS.put("JMSReplyTo", new PropertySetter() {
455            @Override
456            public void set(Message message, Object value) throws MessageFormatException {
457                ActiveMQDestination rc = (ActiveMQDestination) TypeConversionSupport.convert(value, ActiveMQDestination.class);
458                if (rc == null) {
459                    throw new MessageFormatException("Property JMSReplyTo cannot be set from a " + value.getClass().getName() + ".");
460                }
461                ((ActiveMQMessage) message).setReplyTo(rc);
462            }
463        });
464        JMS_PROPERTY_SETERS.put("JMSTimestamp", new PropertySetter() {
465            @Override
466            public void set(Message message, Object value) throws MessageFormatException {
467                Long rc = (Long) TypeConversionSupport.convert(value, Long.class);
468                if (rc == null) {
469                    throw new MessageFormatException("Property JMSTimestamp cannot be set from a " + value.getClass().getName() + ".");
470                }
471                ((ActiveMQMessage) message).setJMSTimestamp(rc.longValue());
472            }
473        });
474        JMS_PROPERTY_SETERS.put("JMSType", new PropertySetter() {
475            @Override
476            public void set(Message message, Object value) throws MessageFormatException {
477                String rc = (String) TypeConversionSupport.convert(value, String.class);
478                if (rc == null) {
479                    throw new MessageFormatException("Property JMSType cannot be set from a " + value.getClass().getName() + ".");
480                }
481                ((ActiveMQMessage) message).setJMSType(rc);
482            }
483        });
484    }
485
486    @Override
487    public void setObjectProperty(String name, Object value) throws JMSException {
488        setObjectProperty(name, value, true);
489    }
490
491    public void setObjectProperty(String name, Object value, boolean checkReadOnly) throws JMSException {
492
493        if (checkReadOnly) {
494            checkReadOnlyProperties();
495        }
496        if (name == null || name.equals("")) {
497            throw new IllegalArgumentException("Property name cannot be empty or null");
498        }
499
500        if (value instanceof UTF8Buffer) {
501            value = value.toString();
502        }
503
504        checkValidObject(value);
505        value = convertScheduled(name, value);
506        PropertySetter setter = JMS_PROPERTY_SETERS.get(name);
507
508        if (setter != null && value != null) {
509            setter.set(this, value);
510        } else {
511            try {
512                this.setProperty(name, value);
513            } catch (IOException e) {
514                throw JMSExceptionSupport.create(e);
515            }
516        }
517    }
518
519    public void setProperties(Map<String, ?> properties) throws JMSException {
520        for (Map.Entry<String, ?> entry : properties.entrySet()) {
521            // Lets use the object property method as we may contain standard
522            // extension headers like JMSXGroupID
523            setObjectProperty(entry.getKey(), entry.getValue());
524        }
525    }
526
527    protected void checkValidObject(Object value) throws MessageFormatException {
528
529        boolean valid = value instanceof Boolean || value instanceof Byte || value instanceof Short || value instanceof Integer || value instanceof Long;
530        valid = valid || value instanceof Float || value instanceof Double || value instanceof Character || value instanceof String || value == null;
531
532        if (!valid) {
533
534            ActiveMQConnection conn = getConnection();
535            // conn is null if we are in the broker rather than a JMS client
536            if (conn == null || conn.isNestedMapAndListEnabled()) {
537                if (!(value instanceof Map || value instanceof List)) {
538                    throw new MessageFormatException("Only objectified primitive objects, String, Map and List types are allowed but was: " + value + " type: " + value.getClass());
539                }
540            } else {
541                throw new MessageFormatException("Only objectified primitive objects and String types are allowed but was: " + value + " type: " + value.getClass());
542            }
543        }
544    }
545
546    protected Object convertScheduled(String name, Object value) throws MessageFormatException {
547        Object result = value;
548        if (AMQ_SCHEDULED_DELAY.equals(name)){
549            result = TypeConversionSupport.convert(value, Long.class);
550            if (result != null && (Long)result < 0) {
551                throw new MessageFormatException(name + " must not be a negative value");
552            }
553        }
554        else if (AMQ_SCHEDULED_PERIOD.equals(name)){
555            result = TypeConversionSupport.convert(value, Long.class);
556            if (result != null && (Long)result < 0) {
557                throw new MessageFormatException(name + " must not be a negative value");
558            }
559        }
560        else if (AMQ_SCHEDULED_REPEAT.equals(name)){
561            result = TypeConversionSupport.convert(value, Integer.class);
562            if (result != null && (Integer)result < 0) {
563                throw new MessageFormatException(name + " must not be a negative value");
564            }
565        }
566        else if (AMQ_SCHEDULED_CRON.equals(name)) {
567            CronParser.validate(value.toString());
568        }
569        return result;
570    }
571
572    @Override
573    public Object getObjectProperty(String name) throws JMSException {
574        if (name == null) {
575            throw new NullPointerException("Property name cannot be null");
576        }
577
578        // PropertyExpression handles converting message headers to properties.
579        PropertyExpression expression = new PropertyExpression(name);
580        return expression.evaluate(this);
581    }
582
583    @Override
584    public boolean getBooleanProperty(String name) throws JMSException {
585        Object value = getObjectProperty(name);
586        if (value == null) {
587            return false;
588        }
589        Boolean rc = (Boolean) TypeConversionSupport.convert(value, Boolean.class);
590        if (rc == null) {
591            throw new MessageFormatException("Property " + name + " was a " + value.getClass().getName() + " and cannot be read as a boolean");
592        }
593        return rc.booleanValue();
594    }
595
596    @Override
597    public byte getByteProperty(String name) throws JMSException {
598        Object value = getObjectProperty(name);
599        if (value == null) {
600            throw new NumberFormatException("property " + name + " was null");
601        }
602        Byte rc = (Byte) TypeConversionSupport.convert(value, Byte.class);
603        if (rc == null) {
604            throw new MessageFormatException("Property " + name + " was a " + value.getClass().getName() + " and cannot be read as a byte");
605        }
606        return rc.byteValue();
607    }
608
609    @Override
610    public short getShortProperty(String name) throws JMSException {
611        Object value = getObjectProperty(name);
612        if (value == null) {
613            throw new NumberFormatException("property " + name + " was null");
614        }
615        Short rc = (Short) TypeConversionSupport.convert(value, Short.class);
616        if (rc == null) {
617            throw new MessageFormatException("Property " + name + " was a " + value.getClass().getName() + " and cannot be read as a short");
618        }
619        return rc.shortValue();
620    }
621
622    @Override
623    public int getIntProperty(String name) throws JMSException {
624        Object value = getObjectProperty(name);
625        if (value == null) {
626            throw new NumberFormatException("property " + name + " was null");
627        }
628        Integer rc = (Integer) TypeConversionSupport.convert(value, Integer.class);
629        if (rc == null) {
630            throw new MessageFormatException("Property " + name + " was a " + value.getClass().getName() + " and cannot be read as an integer");
631        }
632        return rc.intValue();
633    }
634
635    @Override
636    public long getLongProperty(String name) throws JMSException {
637        Object value = getObjectProperty(name);
638        if (value == null) {
639            throw new NumberFormatException("property " + name + " was null");
640        }
641        Long rc = (Long) TypeConversionSupport.convert(value, Long.class);
642        if (rc == null) {
643            throw new MessageFormatException("Property " + name + " was a " + value.getClass().getName() + " and cannot be read as a long");
644        }
645        return rc.longValue();
646    }
647
648    @Override
649    public float getFloatProperty(String name) throws JMSException {
650        Object value = getObjectProperty(name);
651        if (value == null) {
652            throw new NullPointerException("property " + name + " was null");
653        }
654        Float rc = (Float) TypeConversionSupport.convert(value, Float.class);
655        if (rc == null) {
656            throw new MessageFormatException("Property " + name + " was a " + value.getClass().getName() + " and cannot be read as a float");
657        }
658        return rc.floatValue();
659    }
660
661    @Override
662    public double getDoubleProperty(String name) throws JMSException {
663        Object value = getObjectProperty(name);
664        if (value == null) {
665            throw new NullPointerException("property " + name + " was null");
666        }
667        Double rc = (Double) TypeConversionSupport.convert(value, Double.class);
668        if (rc == null) {
669            throw new MessageFormatException("Property " + name + " was a " + value.getClass().getName() + " and cannot be read as a double");
670        }
671        return rc.doubleValue();
672    }
673
674    @Override
675    public String getStringProperty(String name) throws JMSException {
676        Object value = null;
677        if ("JMSXUserID".equals(name)) {
678            value = getUserID();
679            if (value == null) {
680                value = getObjectProperty(name);
681            }
682        } else {
683            value = getObjectProperty(name);
684        }
685        if (value == null) {
686            return null;
687        }
688        String rc = (String) TypeConversionSupport.convert(value, String.class);
689        if (rc == null) {
690            throw new MessageFormatException("Property " + name + " was a " + value.getClass().getName() + " and cannot be read as a String");
691        }
692        return rc;
693    }
694
695    @Override
696    public void setBooleanProperty(String name, boolean value) throws JMSException {
697        setBooleanProperty(name, value, true);
698    }
699
700    public void setBooleanProperty(String name, boolean value, boolean checkReadOnly) throws JMSException {
701        setObjectProperty(name, Boolean.valueOf(value), checkReadOnly);
702    }
703
704    @Override
705    public void setByteProperty(String name, byte value) throws JMSException {
706        setObjectProperty(name, Byte.valueOf(value));
707    }
708
709    @Override
710    public void setShortProperty(String name, short value) throws JMSException {
711        setObjectProperty(name, Short.valueOf(value));
712    }
713
714    @Override
715    public void setIntProperty(String name, int value) throws JMSException {
716        setObjectProperty(name, Integer.valueOf(value));
717    }
718
719    @Override
720    public void setLongProperty(String name, long value) throws JMSException {
721        setObjectProperty(name, Long.valueOf(value));
722    }
723
724    @Override
725    public void setFloatProperty(String name, float value) throws JMSException {
726        setObjectProperty(name, Float.valueOf(value));
727    }
728
729    @Override
730    public void setDoubleProperty(String name, double value) throws JMSException {
731        setObjectProperty(name, Double.valueOf(value));
732    }
733
734    @Override
735    public void setStringProperty(String name, String value) throws JMSException {
736        setObjectProperty(name, value);
737    }
738
739    protected void checkReadOnlyProperties() throws MessageNotWriteableException {
740        if (readOnlyProperties) {
741            throw new MessageNotWriteableException("Message properties are read-only");
742        }
743    }
744
745    protected void checkReadOnlyBody() throws MessageNotWriteableException {
746        if (readOnlyBody) {
747            throw new MessageNotWriteableException("Message body is read-only");
748        }
749    }
750
751    public Callback getAcknowledgeCallback() {
752        return acknowledgeCallback;
753    }
754
755    public void setAcknowledgeCallback(Callback acknowledgeCallback) {
756        this.acknowledgeCallback = acknowledgeCallback;
757    }
758
759    /**
760     * Send operation event listener. Used to get the message ready to be sent.
761     */
762    public void onSend() throws JMSException {
763        setReadOnlyBody(true);
764        setReadOnlyProperties(true);
765    }
766
767    @Override
768    public Response visit(CommandVisitor visitor) throws Exception {
769        return visitor.processMessage(this);
770    }
771
772    @Override
773    public void storeContent() {
774    }
775
776    @Override
777    public void storeContentAndClear() {
778        storeContent();
779    }
780
781    @Override
782    protected boolean isContentMarshalled() {
783        //Always return true because ActiveMQMessage only has a content field
784        //which is already marshalled
785        return true;
786    }
787}