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.transport.amqp;
018
019import java.io.IOException;
020import java.net.URI;
021import java.nio.ByteBuffer;
022import java.security.cert.X509Certificate;
023
024import org.apache.activemq.transport.TransportSupport;
025import org.apache.activemq.transport.amqp.AmqpFrameParser.AMQPFrameSink;
026import org.apache.activemq.transport.ws.WSTransport;
027import org.apache.activemq.util.IOExceptionSupport;
028import org.apache.activemq.util.ServiceStopper;
029import org.apache.activemq.wireformat.WireFormat;
030
031/**
032 * An AMQP based WebSocket transport implementation.
033 */
034public class AmqpWSTransport extends TransportSupport implements WSTransport, AMQPFrameSink {
035
036    private final AmqpFrameParser frameReader = new AmqpFrameParser(this);
037    private final URI remoteLocation;
038
039    private WSTransportSink outputSink;
040    private int receiveCounter;
041    private X509Certificate[] certificates;
042
043    /**
044     * Create a new Transport instance.
045     *
046     * @param location
047     *      the remote location where the client connection is from.
048     * @param wireFormat
049     *      the WireFormat instance that configures this Transport.
050     */
051    public AmqpWSTransport(URI location, WireFormat wireFormat) {
052        super();
053
054        remoteLocation = location;
055        frameReader.setWireFormat((AmqpWireFormat) wireFormat);
056    }
057
058    @Override
059    public void setTransportSink(WSTransportSink outputSink) {
060        this.outputSink = outputSink;
061    }
062
063    @Override
064    public void oneway(Object command) throws IOException {
065        if (command instanceof ByteBuffer) {
066            outputSink.onSocketOutboundBinary((ByteBuffer) command);
067        } else {
068            throw new IOException("Unexpected output command.");
069        }
070    }
071
072    @Override
073    public String getRemoteAddress() {
074        return remoteLocation.toASCIIString();
075    }
076
077    @Override
078    public int getReceiveCounter() {
079        return receiveCounter;
080    }
081
082    @Override
083    public X509Certificate[] getPeerCertificates() {
084        return certificates;
085    }
086
087    @Override
088    public void setPeerCertificates(X509Certificate[] certificates) {
089        this.certificates = certificates;
090    }
091
092    @Override
093    public String getSubProtocol() {
094        return "amqp";
095    }
096
097    @Override
098    public WireFormat getWireFormat() {
099        return frameReader.getWireFormat();
100    }
101
102    @Override
103    public int getMaxFrameSize() {
104        return (int) Math.min(((AmqpWireFormat) getWireFormat()).getMaxFrameSize(), Integer.MAX_VALUE);
105    }
106
107    @Override
108    protected void doStop(ServiceStopper stopper) throws Exception {
109        // Currently nothing needed here since we have no async workers.
110    }
111
112    @Override
113    protected void doStart() throws Exception {
114        if (outputSink == null) {
115            throw new IllegalStateException("Transport started before output sink assigned.");
116        }
117
118        // Currently nothing needed here since we have no async workers.
119    }
120
121    //----- WebSocket event hooks --------------------------------------------//
122
123    @Override
124    public void onWebSocketText(String data) throws IOException {
125        onException(new IOException("Illegal text content receive on AMQP WebSocket channel."));
126    }
127
128    @Override
129    public void onWebSocketBinary(ByteBuffer data) throws IOException {
130        try {
131            frameReader.parse(data);
132        } catch (Exception e) {
133            throw IOExceptionSupport.create(e);
134        }
135    }
136
137    @Override
138    public void onWebSocketClosed() throws IOException {
139        onException(new IOException("Unexpected close of AMQP WebSocket channel."));
140    }
141
142    //----- AMQP Frame Data event hook ---------------------------------------//
143
144    @Override
145    public void onFrame(Object frame) {
146        doConsume(frame);
147    }
148}