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 void checkValidScheduled(String name, Object value) throws MessageFormatException {
547        if (AMQ_SCHEDULED_DELAY.equals(name) || AMQ_SCHEDULED_PERIOD.equals(name) || AMQ_SCHEDULED_REPEAT.equals(name)) {
548            if (value instanceof Long == false && value instanceof Integer == false) {
549                throw new MessageFormatException(name + " should be long or int value");
550            }
551        }
552        if (AMQ_SCHEDULED_CRON.equals(name)) {
553            CronParser.validate(value.toString());
554        }
555    }
556
557    protected Object convertScheduled(String name, Object value) throws MessageFormatException {
558        Object result = value;
559        if (AMQ_SCHEDULED_DELAY.equals(name)){
560            result = TypeConversionSupport.convert(value, Long.class);
561        }
562        else if (AMQ_SCHEDULED_PERIOD.equals(name)){
563            result = TypeConversionSupport.convert(value, Long.class);
564        }
565        else if (AMQ_SCHEDULED_REPEAT.equals(name)){
566            result = TypeConversionSupport.convert(value, Integer.class);
567        }
568        return result;
569    }
570
571    @Override
572    public Object getObjectProperty(String name) throws JMSException {
573        if (name == null) {
574            throw new NullPointerException("Property name cannot be null");
575        }
576
577        // PropertyExpression handles converting message headers to properties.
578        PropertyExpression expression = new PropertyExpression(name);
579        return expression.evaluate(this);
580    }
581
582    @Override
583    public boolean getBooleanProperty(String name) throws JMSException {
584        Object value = getObjectProperty(name);
585        if (value == null) {
586            return false;
587        }
588        Boolean rc = (Boolean) TypeConversionSupport.convert(value, Boolean.class);
589        if (rc == null) {
590            throw new MessageFormatException("Property " + name + " was a " + value.getClass().getName() + " and cannot be read as a boolean");
591        }
592        return rc.booleanValue();
593    }
594
595    @Override
596    public byte getByteProperty(String name) throws JMSException {
597        Object value = getObjectProperty(name);
598        if (value == null) {
599            throw new NumberFormatException("property " + name + " was null");
600        }
601        Byte rc = (Byte) TypeConversionSupport.convert(value, Byte.class);
602        if (rc == null) {
603            throw new MessageFormatException("Property " + name + " was a " + value.getClass().getName() + " and cannot be read as a byte");
604        }
605        return rc.byteValue();
606    }
607
608    @Override
609    public short getShortProperty(String name) throws JMSException {
610        Object value = getObjectProperty(name);
611        if (value == null) {
612            throw new NumberFormatException("property " + name + " was null");
613        }
614        Short rc = (Short) TypeConversionSupport.convert(value, Short.class);
615        if (rc == null) {
616            throw new MessageFormatException("Property " + name + " was a " + value.getClass().getName() + " and cannot be read as a short");
617        }
618        return rc.shortValue();
619    }
620
621    @Override
622    public int getIntProperty(String name) throws JMSException {
623        Object value = getObjectProperty(name);
624        if (value == null) {
625            throw new NumberFormatException("property " + name + " was null");
626        }
627        Integer rc = (Integer) TypeConversionSupport.convert(value, Integer.class);
628        if (rc == null) {
629            throw new MessageFormatException("Property " + name + " was a " + value.getClass().getName() + " and cannot be read as an integer");
630        }
631        return rc.intValue();
632    }
633
634    @Override
635    public long getLongProperty(String name) throws JMSException {
636        Object value = getObjectProperty(name);
637        if (value == null) {
638            throw new NumberFormatException("property " + name + " was null");
639        }
640        Long rc = (Long) TypeConversionSupport.convert(value, Long.class);
641        if (rc == null) {
642            throw new MessageFormatException("Property " + name + " was a " + value.getClass().getName() + " and cannot be read as a long");
643        }
644        return rc.longValue();
645    }
646
647    @Override
648    public float getFloatProperty(String name) throws JMSException {
649        Object value = getObjectProperty(name);
650        if (value == null) {
651            throw new NullPointerException("property " + name + " was null");
652        }
653        Float rc = (Float) TypeConversionSupport.convert(value, Float.class);
654        if (rc == null) {
655            throw new MessageFormatException("Property " + name + " was a " + value.getClass().getName() + " and cannot be read as a float");
656        }
657        return rc.floatValue();
658    }
659
660    @Override
661    public double getDoubleProperty(String name) throws JMSException {
662        Object value = getObjectProperty(name);
663        if (value == null) {
664            throw new NullPointerException("property " + name + " was null");
665        }
666        Double rc = (Double) TypeConversionSupport.convert(value, Double.class);
667        if (rc == null) {
668            throw new MessageFormatException("Property " + name + " was a " + value.getClass().getName() + " and cannot be read as a double");
669        }
670        return rc.doubleValue();
671    }
672
673    @Override
674    public String getStringProperty(String name) throws JMSException {
675        Object value = null;
676        if (name.equals("JMSXUserID")) {
677            value = getUserID();
678            if (value == null) {
679                value = getObjectProperty(name);
680            }
681        } else {
682            value = getObjectProperty(name);
683        }
684        if (value == null) {
685            return null;
686        }
687        String rc = (String) TypeConversionSupport.convert(value, String.class);
688        if (rc == null) {
689            throw new MessageFormatException("Property " + name + " was a " + value.getClass().getName() + " and cannot be read as a String");
690        }
691        return rc;
692    }
693
694    @Override
695    public void setBooleanProperty(String name, boolean value) throws JMSException {
696        setBooleanProperty(name, value, true);
697    }
698
699    public void setBooleanProperty(String name, boolean value, boolean checkReadOnly) throws JMSException {
700        setObjectProperty(name, Boolean.valueOf(value), checkReadOnly);
701    }
702
703    @Override
704    public void setByteProperty(String name, byte value) throws JMSException {
705        setObjectProperty(name, Byte.valueOf(value));
706    }
707
708    @Override
709    public void setShortProperty(String name, short value) throws JMSException {
710        setObjectProperty(name, Short.valueOf(value));
711    }
712
713    @Override
714    public void setIntProperty(String name, int value) throws JMSException {
715        setObjectProperty(name, Integer.valueOf(value));
716    }
717
718    @Override
719    public void setLongProperty(String name, long value) throws JMSException {
720        setObjectProperty(name, Long.valueOf(value));
721    }
722
723    @Override
724    public void setFloatProperty(String name, float value) throws JMSException {
725        setObjectProperty(name, new Float(value));
726    }
727
728    @Override
729    public void setDoubleProperty(String name, double value) throws JMSException {
730        setObjectProperty(name, new Double(value));
731    }
732
733    @Override
734    public void setStringProperty(String name, String value) throws JMSException {
735        setObjectProperty(name, value);
736    }
737
738    private void checkReadOnlyProperties() throws MessageNotWriteableException {
739        if (readOnlyProperties) {
740            throw new MessageNotWriteableException("Message properties are read-only");
741        }
742    }
743
744    protected void checkReadOnlyBody() throws MessageNotWriteableException {
745        if (readOnlyBody) {
746            throw new MessageNotWriteableException("Message body is read-only");
747        }
748    }
749
750    public Callback getAcknowledgeCallback() {
751        return acknowledgeCallback;
752    }
753
754    public void setAcknowledgeCallback(Callback acknowledgeCallback) {
755        this.acknowledgeCallback = acknowledgeCallback;
756    }
757
758    /**
759     * Send operation event listener. Used to get the message ready to be sent.
760     */
761    public void onSend() throws JMSException {
762        setReadOnlyBody(true);
763        setReadOnlyProperties(true);
764    }
765
766    @Override
767    public Response visit(CommandVisitor visitor) throws Exception {
768        return visitor.processMessage(this);
769    }
770
771    @Override
772    public void storeContent() {
773    }
774
775    @Override
776    public void storeContentAndClear() {
777        storeContent();
778    }
779}