All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.lightbend.lagom.javadsl.api.transport.MessageProtocol Maven / Gradle / Ivy

There is a newer version: 1.5.5
Show newest version
/*
 * Copyright (C) 2016 Lightbend Inc. 
 */
package com.lightbend.lagom.javadsl.api.transport;

import java.nio.charset.Charset;
import java.util.Optional;

/**
 * A message protocol.
 *
 * This describes the negotiated protocol being used for a message.  It has three elements, a content type, a charset,
 * and a version.
 *
 * The contentType may be registered mime type such as application/json, or it could be an application
 * specific content type, such as application/vnd.myservice+json.  It could also contain protocol versioning
 * information, such as application/vnd.github.v3+json.  During the protocol negotiation process, the
 * content type may be transformed, for example, if the content type contains a version, the configured
 * {@link HeaderFilter} will be expected to extract that version
 * out into the version, leaving a contentType that will be understood by the message serializer.
 *
 * The charset applies to text messages, if the message is not in a text format, then no charset
 * should be specified.  This is not only used in setting of content negotiation headers, it's also used as a hint to
 * the framework of when it can treat a message as text.  For example, if the charset is set, then when a message gets
 * sent via WebSockets, it will be sent as a text message, otherwise it will be sent as a binary message.
 *
 * The version is used to describe the version of the protocol being used. Lagom does not, out of the box,
 * prescribe any semantics around the version, from Lagom's perspective, two message protocols with different versions
 * are two different protocols. The version is however treated as a separate piece of information so that generic
 * parsers, such as json/xml, can make sensible use of the content type passed to them.  The version could come from
 * a media type header, but it does not necessarily have to come from there, it could come from the URI or any other
 * header.
 *
 * MessageProtocol instances can also be used in content negotiation, an empty value means that any value
 * is accepted.
 */
public final class MessageProtocol {

    private final Optional contentType;
    private final Optional charset;
    private final Optional version;

    /**
     * Create a message protocol with the given content type, charset and version.
     *
     * @param contentType The content type.
     * @param charset The charset.
     * @param version The version.
     */
    public MessageProtocol(Optional contentType, Optional charset, Optional version) {
        this.contentType = contentType;
        this.charset = charset;
        this.version = version;
    }

    /**
     * Create a message protocol that doesn't specify any content type, charset or version.
     */
    public MessageProtocol() {
        contentType = Optional.empty();
        charset = Optional.empty();
        version = Optional.empty();
    }

    /**
     * The content type of the protocol.
     *
     * @return The content type.
     */
    public Optional contentType() {
        return contentType;
    }

    /**
     * The charset of the protocol.
     *
     * @return The charset.
     */
    public Optional charset() {
        return charset;
    }

    /**
     * The version of the protocol.
     *
     * @return The version.
     */
    public Optional version() {
        return version;
    }

    /**
     * Return a copy of this message protocol with the content type set to the given content type.
     *
     * @param contentType The content type to set.
     * @return A copy of this message protocol.
     */
    public MessageProtocol withContentType(String contentType) {
        return new MessageProtocol(Optional.ofNullable(contentType), charset, version);
    }

    /**
     * Return a copy of this message protocol with the charset set to the given charset.
     *
     * @param charset The charset to set.
     * @return A copy of this message protocol.
     */
    public MessageProtocol withCharset(String charset) {
        return new MessageProtocol(contentType, Optional.ofNullable(charset), version);
    }

    /**
     * Return a copy of this message protocol with the version set to the given version.
     *
     * @param version The version to set.
     * @return A copy of this message protocol.
     */
    public MessageProtocol withVersion(String version) {
        return new MessageProtocol(contentType, charset, Optional.ofNullable(version));
    }

    /**
     * Whether this message protocol is a text based protocol.
     *
     * This is determined by whether the charset is defined.
     *
     * @return true if this message protocol is text based.
     */
    public boolean isText() {
        return charset.isPresent();
    }

    private static final Charset utf8Charset = Charset.forName("utf-8");

    /**
     * Whether the protocol uses UTF-8.
     *
     * @return true if the charset used by this protocol is UTF-8, false if it's some other encoding or if no charset is
     *      defined.
     */
    public boolean isUtf8() {
        if (charset.isPresent()) {
            return utf8Charset.equals(Charset.forName(charset.get()));
        } else {
            return false;
        }
    }

    /**
     * Convert this message protocol to a content type header, if the content type is defined.
     *
     * @return The message protocol as a content type header.
     */
    public Optional toContentTypeHeader() {
        return contentType.map(ct -> {
            if (charset.isPresent()) {
                return ct + "; charset=" + charset.get();
            } else {
                return ct;
            }
        });
    }

    /**
     * Parse a message protocol from a content type header, if defined.
     *
     * @param contentType The content type header to parse.
     * @return The parsed message protocol.
     */
    public static MessageProtocol fromContentTypeHeader(Optional contentType) {
        return contentType.map(ct -> {
            String[] parts = ct.split(";");
            String justContentType = parts[0];
            Optional charset = Optional.empty();
            for (int i = 1; i < parts.length; i++) {
                String toTest = parts[i].trim();
                if (toTest.startsWith("charset=")) {
                    charset = Optional.of(toTest.split("=", 2)[1]);
                    break;
                }
            }
            return new MessageProtocol(Optional.of(justContentType), charset, Optional.empty());
        }).orElse(new MessageProtocol());
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        MessageProtocol that = (MessageProtocol) o;

        if (!contentType.equals(that.contentType)) return false;
        if (!charset.equals(that.charset)) return false;
        return version.equals(that.version);

    }

    @Override
    public int hashCode() {
        int result = contentType.hashCode();
        result = 31 * result + charset.hashCode();
        result = 31 * result + version.hashCode();
        return result;
    }

    @Override
    public String toString() {
        return "MessageProtocol{" +
                "contentType=" + contentType +
                ", charset=" + charset +
                ", version=" + version +
                '}';
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy