/**
*
* Copyright 2004 Hiram Chirino
* Copyright 2004 Protique Ltd
*
* 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.codehaus.activemq.transport.gnet;
import EDU.oswego.cs.dl.util.concurrent.Latch;
import EDU.oswego.cs.dl.util.concurrent.SynchronizedBoolean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.geronimo.network.SelectorManager;
import org.apache.geronimo.network.protocol.AbstractProtocol;
import org.apache.geronimo.network.protocol.DownPacket;
import org.apache.geronimo.network.protocol.PlainDownPacket;
import org.apache.geronimo.network.protocol.Protocol;
import org.apache.geronimo.network.protocol.ProtocolException;
import org.apache.geronimo.network.protocol.SocketProtocol;
import org.apache.geronimo.network.protocol.UpPacket;
import org.apache.geronimo.pool.ClockPool;
import org.apache.geronimo.pool.ThreadPool;
import org.codehaus.activemq.message.Packet;
import org.codehaus.activemq.message.WireFormat;
import org.codehaus.activemq.transport.TransportChannelSupport;
import javax.jms.JMSException;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
/**
* An implementation of a TransportChannel which uses the Geronimo network layer
* for connectivity.
*
* @version $Revision: 1.16 $
*/
public class GTransportChannel extends TransportChannelSupport {
private static final Log log = LogFactory.getLog(GTransportChannel.class);
private SynchronizedBoolean closed;
private SynchronizedBoolean started;
private Protocol protocol;
private Latch dispatchLatch;
private ThreadPool threadPool;
private WireFormat wireFormat;
/**
* Construct basic helpers
*/
protected GTransportChannel(WireFormat wireFormat, ThreadPool tp) {
this.wireFormat = wireFormat;
closed = new SynchronizedBoolean(false);
started = new SynchronizedBoolean(false);
dispatchLatch = new Latch();
threadPool = tp;
}
/**
* @param protocol
*/
public GTransportChannel(WireFormat wireFormat, Protocol protocol, ThreadPool tp) {
this(wireFormat, tp);
init(protocol);
}
/**
* @param remoteLocation
* @param localLocation
*/
public GTransportChannel(WireFormat wireFormat, URI remoteLocation, URI localLocation,
SelectorManager sm, ThreadPool tp, ClockPool cp)
throws UnknownHostException, ProtocolException {
this(wireFormat, tp);
/*
ControlClientProtocolStack clientStack = new ControlClientProtocolStack();
clientStack.setClassLoader(Thread.currentThread()
.getContextClassLoader());
clientStack.setThreadPool(tp);
clientStack.setClockPool(cp);
clientStack.setSelectorManager(sm);
*/
SocketProtocol sp = new SocketProtocol();
sp.setTimeout(1000 * 30);
if (localLocation != null) {
sp.setInterface(new InetSocketAddress(InetAddress
.getByName(localLocation.getHost()), localLocation
.getPort()));
}
sp.setAddress(new InetSocketAddress(InetAddress
.getByName(remoteLocation.getHost()), remoteLocation
.getPort()));
sp.setSelectorManager(sm);
/*
clientStack.push(sp);
ControlClientProtocol ccp = new ControlClientProtocol();
ccp.setTimeout(1000 * 30);
clientStack.push(ccp);
clientStack.setup();
*/
// init(clientStack);
init(sp);
sp.setup();
}
/**
* @param protocol
*/
private void init(Protocol protocol) {
this.protocol = protocol;
// Hookup a new Up protocol so we can get the up stream packets.
protocol.setUpProtocol(new AbstractProtocol() {
public void setup() {
}
public void drain() {
}
public void teardown() {
}
public void sendUp(final UpPacket p) {
try {
log.trace("AQUIRING: " + dispatchLatch);
dispatchLatch.acquire();
log.trace("AQUIRED: " + dispatchLatch);
dispatch(p);
}
catch (InterruptedException e) {
log.warn("Caught exception dispatching packet: " + p + ". Reason: "
+ e, e);
// TODO: notify exception listner and close the connection.
}
}
public void sendDown(DownPacket p) throws ProtocolException {
getDownProtocol().sendDown(p);
}
public void flush() throws ProtocolException {
getDownProtocol().flush();
}
});
}
private void dispatch(UpPacket p) {
try {
// Dont dispatch messages until the channel is started..
Packet packet = toPacket(p);
log.trace("<<<< SENDING UP <<<< " + packet);
if (packet != null) {
doConsumePacket(packet);
}
}
catch (IOException e) {
log.warn("Caught exception dispatching packet: " + p + ". Reason: "
+ e, e);
// TODO: notify exception listner and close the connection.
}
}
/**
* close the channel
*/
public void stop() {
super.stop();
if (closed.commit(false, true)) {
try {
protocol.drain();
}
catch (Exception e) {
log.trace(toString() + " now closed");
}
}
}
/**
* start listeneing for events
*
* @throws JMSException if an error occurs
*/
public void start() throws JMSException {
if (started.commit(false, true)) {
// Allow messages to get dispatched.
dispatchLatch.release();
}
}
/**
* Asynchronously send a Packet
*
* @param packet
* @throws JMSException
*/
public void asyncSend(Packet packet) throws JMSException {
try {
if (log.isTraceEnabled()) {
log.trace(">>>> ASYNC SENDING DOWN >>>> " + packet);
}
// lets sync for now to avoid multiple threads writing to the same socket
synchronized (protocol) {
protocol.sendDown(toPlainDownPacket(packet));
}
}
catch (IOException e) {
System.out.println("Caught: " + e);
e.printStackTrace();
JMSException jmsEx = new JMSException("asyncSend failed "
+ e.getMessage());
jmsEx.setLinkedException(e);
throw jmsEx;
}
catch (ProtocolException e) {
System.out.println("Caught: " + e);
e.printStackTrace();
JMSException jmsEx = new JMSException("asyncSend failed "
+ e.getMessage());
jmsEx.setLinkedException(e);
throw jmsEx;
}
}
public boolean isMulticast() {
return false;
}
protected PlainDownPacket toPlainDownPacket(Packet mqpacket)
throws IOException, JMSException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
wireFormat.writePacket(mqpacket, dos);
dos.close();
ArrayList list = new ArrayList(1);
ByteBuffer buffer = ByteBuffer.wrap(baos.toByteArray());
buffer.limit(buffer.capacity());
list.add(buffer);
PlainDownPacket packet = new PlainDownPacket();
packet.setBuffers(list);
return packet;
}
protected Packet toPacket(UpPacket packet) throws IOException {
final ByteBuffer buffer = packet.getBuffer();
InputStream is = new InputStream() {
public int read() {
if (!buffer.hasRemaining()) {
return -1;
}
int rc = 0xFF & buffer.get();
return rc;
}
public synchronized int read(byte[] bytes, int off, int len) {
len = Math.min(len, buffer.remaining());
buffer.get(bytes, off, len);
return len;
}
};
DataInputStream dis = new DataInputStream(is);
return wireFormat.readPacket(dis);
}
/**
* pretty print for object
*
* @return String representation of this object
*/
public String toString() {
return "GTransportChannel: " + protocol;
}
/**
* Can this wireformat process packets of this version
* @param version the version number to test
* @return true if can accept the version
*/
public boolean canProcessWireFormatVersion(int version){
return wireFormat.canProcessWireFormatVersion(version);
}
/**
* @return the current version of this wire format
*/
public int getCurrentWireFormatVersion(){
return wireFormat.getCurrentWireFormatVersion();
}
}