/* * 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.transport.amqp; import java.io.IOException; import java.net.URI; import java.nio.ByteBuffer; import java.security.cert.X509Certificate; import org.apache.activemq.transport.TransportSupport; import org.apache.activemq.transport.amqp.AmqpFrameParser.AMQPFrameSink; import org.apache.activemq.transport.ws.WSTransport; import org.apache.activemq.util.IOExceptionSupport; import org.apache.activemq.util.ServiceStopper; import org.apache.activemq.wireformat.WireFormat; /** * An AMQP based WebSocket transport implementation. */ public class AmqpWSTransport extends TransportSupport implements WSTransport, AMQPFrameSink { private final AmqpFrameParser frameReader = new AmqpFrameParser(this); private final URI remoteLocation; private WSTransportSink outputSink; private int receiveCounter; private X509Certificate[] certificates; /** * Create a new Transport instance. * * @param location * the remote location where the client connection is from. * @param wireFormat * the WireFormat instance that configures this Transport. */ public AmqpWSTransport(URI location, WireFormat wireFormat) { super(); remoteLocation = location; frameReader.setWireFormat((AmqpWireFormat) wireFormat); } @Override public void setTransportSink(WSTransportSink outputSink) { this.outputSink = outputSink; } @Override public void oneway(Object command) throws IOException { if (command instanceof ByteBuffer) { outputSink.onSocketOutboundBinary((ByteBuffer) command); } else { throw new IOException("Unexpected output command."); } } @Override public String getRemoteAddress() { return remoteLocation.toASCIIString(); } @Override public int getReceiveCounter() { return receiveCounter; } @Override public X509Certificate[] getPeerCertificates() { return certificates; } @Override public void setPeerCertificates(X509Certificate[] certificates) { this.certificates = certificates; } @Override public String getSubProtocol() { return "amqp"; } @Override public WireFormat getWireFormat() { return frameReader.getWireFormat(); } @Override public int getMaxFrameSize() { return (int) Math.min(((AmqpWireFormat) getWireFormat()).getMaxFrameSize(), Integer.MAX_VALUE); } @Override protected void doStop(ServiceStopper stopper) throws Exception { // Currently nothing needed here since we have no async workers. } @Override protected void doStart() throws Exception { if (outputSink == null) { throw new IllegalStateException("Transport started before output sink assigned."); } // Currently nothing needed here since we have no async workers. } //----- WebSocket event hooks --------------------------------------------// @Override public void onWebSocketText(String data) throws IOException { onException(new IOException("Illegal text content receive on AMQP WebSocket channel.")); } @Override public void onWebSocketBinary(ByteBuffer data) throws IOException { try { frameReader.parse(data); } catch (Exception e) { throw IOExceptionSupport.create(e); } } @Override public void onWebSocketClosed() throws IOException { onException(new IOException("Unexpected close of AMQP WebSocket channel.")); } //----- AMQP Frame Data event hook ---------------------------------------// @Override public void onFrame(Object frame) { doConsume(frame); } }