001/*
002 * This file is part of the JDrupes non-blocking HTTP Codec
003 * Copyright (C) 2016, 2017  Michael N. Lipp
004 *
005 * This program is free software; you can redistribute it and/or modify it 
006 * under the terms of the GNU Lesser General Public License as published
007 * by the Free Software Foundation; either version 3 of the License, or 
008 * (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful, but 
011 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
012 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
013 * License for more details.
014 *
015 * You should have received a copy of the GNU Lesser General Public License along 
016 * with this program; if not, see <http://www.gnu.org/licenses/>.
017 */
018
019package org.jdrupes.httpcodec;
020
021import java.nio.Buffer;
022import java.nio.ByteBuffer;
023import java.util.Optional;
024
025import org.jdrupes.httpcodec.Codec.ProtocolSwitchResult;
026
027/**
028 * An engine that can be used as a server. It has an associated
029 * request decoder and response encoder. Using a {@link ServerEngine}
030 * has two main advantages over using an encoder and decoder
031 * directly. It links encoder and decoder (see 
032 * {@link Encoder#setPeerDecoder(Decoder)} and
033 * {@link Decoder#setPeerEncoder(Encoder)}) and it replaces the encoder 
034 * and decoder if the encoded result indicates a switch.
035 * 
036 * @param <Q> the message header type handled by the decoder (the request)
037 * @param <R> the message header type handled be the encoder (the response)
038 */
039public class ServerEngine<Q extends MessageHeader, R extends MessageHeader>
040        extends Engine {
041
042        private Decoder<?, ?> requestDecoder;
043        private Encoder<?, ?> responseEncoder;
044        
045        /**
046         * Creates a new instance.
047         * 
048         * @param requestDecoder the decoder for the request
049         * @param responseEncoder the encoder for the response
050         */
051        public ServerEngine(Decoder<Q, R> requestDecoder, 
052                        Encoder<R, Q> responseEncoder) {
053                this.requestDecoder = requestDecoder;
054                this.responseEncoder = responseEncoder;
055                responseEncoder.setPeerDecoder(requestDecoder);
056                requestDecoder.setPeerEncoder(responseEncoder);
057        }
058
059        /**
060         * Returns the request decoder.
061         * 
062         * @return the request decoder
063         */
064        @SuppressWarnings("unchecked")
065        public Decoder<Q, R> requestDecoder() {
066                return (Decoder<Q, R>)requestDecoder;
067        }
068
069        /**
070         * Returns the response encoder.
071         * 
072         * @return the response encoder
073         */
074        @SuppressWarnings("unchecked")
075        public Encoder<R, Q> responseEncoder() {
076                return (Encoder<R, Q>)responseEncoder;
077        }
078
079        /**
080         * Returns the type of the messages decoded by this server.
081         * 
082         * @return the value
083         */
084        public Class<? extends MessageHeader> decoding() {
085                return requestDecoder.decoding();
086        }
087        
088        /**
089         * Returns the type of the messages encoded by this server.
090         * 
091         * @return the value
092         */
093        public Class<? extends MessageHeader> encoding() {
094                return responseEncoder.encoding();
095        }
096        
097        /**
098         * Decodes a request sent to the server.
099         * 
100         * @param in the data to decode
101         * @param out the decoded data
102         * @param endOfInput {@code true} if this invocation finishes the message
103         * @return the result
104         * @throws ProtocolException if the input violates the protocol
105         */
106        @SuppressWarnings("unchecked")
107        public Decoder.Result<R> decode(
108                ByteBuffer in, Buffer out, boolean endOfInput)
109                        throws ProtocolException {
110                return (Decoder.Result<R>)requestDecoder.decode(in, out, endOfInput);
111        }
112
113        /**
114         * Encodes a response generated by the server. This method must be used
115         * instead of the encoder's method if the encoder and decoder should adapt
116         * to a protocol switch automatically.
117         * 
118         * @param messageHeader
119         *            the message header
120         * @see Encoder#encode(MessageHeader)
121         */
122        @SuppressWarnings("unchecked")
123        public void encode(R messageHeader) {
124                ((Encoder<R, Q>)responseEncoder).encode(messageHeader);
125        }
126
127        /**
128         * Invokes the encoder's encode method. This method must be used instead of
129         * encoder's method if the encoder and decoder should adapt to a protocol
130         * switch automatically.
131         * 
132         * @param out
133         *            the decoded data
134         * @return the result
135         * @see Encoder#encode(ByteBuffer)
136         */
137        public Encoder.Result encode(ByteBuffer out) {
138                return encode(Codec.EMPTY_IN, out, true);
139        }
140
141        /**
142         * Invokes the encoder's encode method. This method must be used
143         * instead of decoding the encoder's method directly to allow derived
144         * server classes to adapt to any information contained in the message.
145         * 
146         * @param in the data to encode
147         * @param out the encoded data
148         * @param endOfInput {@code true} if this invocation finishes the message
149         * @return the result
150         * @see Encoder#encode(Buffer, ByteBuffer, boolean)
151         */
152        @SuppressWarnings("unchecked")
153        public Encoder.Result encode(
154                Buffer in, ByteBuffer out, boolean endOfInput) {
155                Encoder.Result result = responseEncoder.encode(in, out, endOfInput);
156                if (result instanceof ProtocolSwitchResult) {
157                        ProtocolSwitchResult res = (ProtocolSwitchResult)result;
158                        if (res.newProtocol() != null) {
159                                setSwitchedTo(res.newProtocol());
160                                requestDecoder = res.newDecoder();
161                                responseEncoder = res.newEncoder();
162                                ((Decoder<MessageHeader, MessageHeader>)requestDecoder)
163                                        .setPeerEncoder((Encoder<MessageHeader, MessageHeader>)
164                                                        responseEncoder);
165                                ((Encoder<MessageHeader, MessageHeader>)responseEncoder)
166                                        .setPeerDecoder((Decoder<MessageHeader, MessageHeader>)
167                                                        requestDecoder);
168                        }
169                }
170                return result;
171        }
172
173        /**
174         * Returns the last fully decoded request if it exists.
175         * 
176         * @return the request
177         */
178        @SuppressWarnings("unchecked")
179        public Optional<Q> currentRequest() {
180                return (Optional<Q>)requestDecoder.header();
181        }
182        
183}