content: /*
   * 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.artemis.jms.client;
  
  import javax.jms.JMSException;
  import javax.jms.MessageFormatException;
  import javax.jms.ObjectMessage;
  import java.io.ByteArrayInputStream;
  import java.io.ByteArrayOutputStream;
  import java.io.ObjectOutputStream;
  import java.io.Serializable;
  
  import org.apache.activemq.artemis.api.core.ActiveMQException;
  import org.apache.activemq.artemis.api.core.Message;
  import org.apache.activemq.artemis.api.core.client.ClientMessage;
  import org.apache.activemq.artemis.api.core.client.ClientSession;
  import org.apache.activemq.artemis.utils.ObjectInputStreamWithClassLoader;
  
  /**
   * ActiveMQ Artemis implementation of a JMS ObjectMessage.
   * <p>
   * Don't used ObjectMessage if you want good performance!
   * <p>
   * Serialization is slooooow!
   */
  public class ActiveMQObjectMessage extends ActiveMQMessage implements ObjectMessage {
  
  
     public static final byte TYPE = Message.OBJECT_TYPE;
  
  
     // keep a snapshot of the Serializable Object as a byte[] to provide Object isolation
     private byte[] data;
  
     private final ConnectionFactoryOptions options;
  
  
     protected ActiveMQObjectMessage(final ClientSession session, ConnectionFactoryOptions options) {
        super(ActiveMQObjectMessage.TYPE, session);
        this.options = options;
     }
  
     protected ActiveMQObjectMessage(final ClientMessage message,
                                     final ClientSession session,
                                     ConnectionFactoryOptions options) {
        super(message, session);
        this.options = options;
     }
  
     /**
      * A copy constructor for foreign JMS ObjectMessages.
      */
     public ActiveMQObjectMessage(final ObjectMessage foreign,
                                  final ClientSession session,
                                  ConnectionFactoryOptions options) throws JMSException {
        super(foreign, ActiveMQObjectMessage.TYPE, session);
  
        setObject(foreign.getObject());
        this.options = options;
     }
  
  
     @Override
     public byte getType() {
        return ActiveMQObjectMessage.TYPE;
     }
  
     @Override
     public void doBeforeSend() throws Exception {
        message.getBodyBuffer().clear();
        if (data != null) {
           message.getBodyBuffer().writeInt(data.length);
           message.getBodyBuffer().writeBytes(data);
        }
  
        super.doBeforeSend();
     }
  
     @Override
     public void doBeforeReceive() throws ActiveMQException {
        super.doBeforeReceive();
        try {
           int len = message.getBodyBuffer().readInt();
           data = new byte[len];
           message.getBodyBuffer().readBytes(data);
        } catch (Exception e) {
           data = null;
        }
  
     }
  
     // ObjectMessage implementation ----------------------------------
  
     @Override
     public void setObject(final Serializable object) throws JMSException {
        checkWrite();
  
        if (object != null) {
           try {
              ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
  
              ObjectOutputStream oos = new ObjectOutputStream(baos);
  
              oos.writeObject(object);
  
              oos.flush();
  
              data = baos.toByteArray();
           } catch (Exception e) {
              JMSException je = new JMSException("Failed to serialize object");
              je.setLinkedException(e);
              je.initCause(e);
              throw je;
           }
        }
     }
  
     // lazy deserialize the Object the first time the client requests it
     @Override
     public Serializable getObject() throws JMSException {
        if (data == null || data.length == 0) {
           return null;
        }
  
        try (ObjectInputStreamWithClassLoader ois = new ObjectInputStreamWithClassLoader(new ByteArrayInputStream(data))) {
           String denyList = getDeserializationDenyList();
           if (denyList != null) {
              ois.setDenyList(denyList);
           }
           String allowList = getDeserializationAllowList();
           if (allowList != null) {
              ois.setAllowList(allowList);
           }
           Serializable object = (Serializable) ois.readObject();
           return object;
        } catch (Exception e) {
           JMSException je = new JMSException(e.getMessage());
           je.setStackTrace(e.getStackTrace());
           throw je;
        }
     }
  
     @Override
     public void clearBody() throws JMSException {
        super.clearBody();
  
        data = null;
     }
  
     @Override
     protected <T> T getBodyInternal(Class<T> c) throws MessageFormatException {
        try {
           return (T) getObject();
        } catch (JMSException e) {
           throw new MessageFormatException("Deserialization error on ActiveMQObjectMessage");
        }
     }
  
     @Override
     public boolean isBodyAssignableTo(Class c) {
        if (data == null) // we have no body
           return true;
        try {
           return Serializable.class == c || Object.class == c || c.isInstance(getObject());
        } catch (JMSException e) {
           return false;
        }
     }
  
     private String getDeserializationDenyList() {
        if (options == null) {
           return null;
        } else {
           return options.getDeserializationDenyList();
        }
     }
  
     private String getDeserializationAllowList() {
        if (options == null) {
           return null;
        } else {
           return options.getDeserializationAllowList();
        }
     }
  }
  