/*
 * Copyright (c) 2010-2016. Axon Framework
 *
 * Licensed 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.axonframework.messaging;

import org.axonframework.common.IdentifierFactory;
import org.axonframework.messaging.unitofwork.CurrentUnitOfWork;

import java.util.Map;

/**
 * Generic implementation of a {@link Message} that contains the payload and metadata as unserialized values.
 * <p>
 * If a GenericMessage is created while a {@link org.axonframework.messaging.unitofwork.UnitOfWork} is active it copies
 * over the correlation data of the UnitOfWork to the created message.
 *
 * @author Rene de Waele
 */
public class GenericMessage<T> extends AbstractMessage<T> {

    private static final long serialVersionUID = 7937214711724527316L;
    private final MetaData metaData;
    private final Class<T> payloadType;
    private final T payload;

    /**
     * Constructs a Message for the given {@code payload} using the correlation data of the current Unit of Work, if
     * present.
     *
     * @param payload The payload for the message
     */
    public GenericMessage(T payload) {
        this(payload, MetaData.emptyInstance());
    }

    /**
     * Constructs a Message for the given {@code payload} and {@code meta data}. The given {@code metaData} is
     * merged with the MetaData from the correlation data of the current unit of work, if present.
     * In case the {@code payload == null}, {@link Void} will be used as the {@code payloadType}.
     *
     * @param payload  The payload for the message as a generic {@code T}
     * @param metaData The meta data {@link Map} for the message
     */
    @SuppressWarnings("unchecked")
    public GenericMessage(T payload, Map<String, ?> metaData) {
        this(payload != null ? (Class<T>) payload.getClass() : (Class<T>) Void.class, payload, metaData);
    }

    /**
     * Constructs a Message for the given {@code payload} and {@code meta data}. The given {@code metaData} is
     * merged with the MetaData from the correlation data of the current unit of work, if present.
     *
     * @param declaredPayloadType The declared type of message payload
     * @param payload             The payload for the message
     * @param metaData            The meta data for the message
     */
    public GenericMessage(Class<T> declaredPayloadType, T payload, Map<String, ?> metaData) {
        this(IdentifierFactory.getInstance().generateIdentifier(), declaredPayloadType, payload,
             CurrentUnitOfWork.correlationData().mergedWith(MetaData.from(metaData)));
    }

    /**
     * Constructor to reconstruct a Message using existing data. Note that no correlation data
     * from a UnitOfWork is attached when using this constructor. If you're constructing a new
     * Message, use {@link #GenericMessage(Object, Map)} instead.
     *
     * @param identifier The identifier of the Message
     * @param payload    The payload of the message
     * @param metaData   The meta data of the message
     * @throws NullPointerException when the given {@code payload} is {@code null}.
     */
    @SuppressWarnings("unchecked")
    public GenericMessage(String identifier, T payload, Map<String, ?> metaData) {
        this(identifier, (Class<T>) payload.getClass(), payload, metaData);
    }

    /**
     * Constructor to reconstruct a Message using existing data. Note that no correlation data
     * from a UnitOfWork is attached when using this constructor. If you're constructing a new
     * Message, use {@link #GenericMessage(Object, Map)} instead
     *
     * @param identifier          The identifier of the Message
     * @param declaredPayloadType The declared type of message payload
     * @param payload             The payload for the message
     * @param metaData            The meta data for the message
     */
    public GenericMessage(String identifier, Class<T> declaredPayloadType, T payload, Map<String, ?> metaData) {
        super(identifier);
        this.metaData = MetaData.from(metaData);
        this.payload = payload;
        this.payloadType = declaredPayloadType;
    }

    private GenericMessage(GenericMessage<T> original, MetaData metaData) {
        super(original.getIdentifier());
        this.payload = original.getPayload();
        this.payloadType = original.getPayloadType();
        this.metaData = metaData;
    }

    @Override
    public MetaData getMetaData() {
        return metaData;
    }

    @Override
    public T getPayload() {
        return payload;
    }

    @Override
    public Class<T> getPayloadType() {
        return payloadType;
    }

    @Override
    protected Message<T> withMetaData(MetaData metaData) {
        return new GenericMessage<>(this, metaData);
    }
}

@@ -2250,79 +2250,50 @@
 his(
-payload != null ? (Class%3CT%3E) payload.getClass() : (Class%3CT%3E) Void.class
+(Class%3CT%3E) getDeclaredPayloadType(payload)
 , pa
@@ -3714,46 +3714,543 @@
 T%3E) 
-payload.getClass(), payload, metaData)
+getDeclaredPayloadType(payload), payload, metaData);%0A    %7D%0A%0A    /**%0A     * Extract the %7B@link Class%7D of the provided %7B@code payload%7D. If %7B@code payload == null%7D this function returns%0A     * %7B@link Void%7D as the payload type.%0A     *%0A     * @param payload the payload of this %7B@link Message%7D%0A     * @return the declared type of the given %7B@code payload%7D or %7B@link Void%7D if %7B@code payload == null%7D%0A     */%0A    private static Class%3C?%3E getDeclaredPayloadType(Object payload) %7B%0A        return payload != null ? payload.getClass() : Void.class
 ;%0A  

Create a static getDeclaredPayloadType() function