1 /***
2 *
3 * Copyright 2004 Hiram Chirino
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.activeio.adapter;
18
19 import java.io.IOException;
20 import java.io.InterruptedIOException;
21
22 import org.activeio.AsynchChannel;
23 import org.activeio.ChannelFactory;
24 import org.activeio.FilterAsynchChannel;
25 import org.activeio.Packet;
26 import org.activeio.PacketData;
27 import org.activeio.RequestChannel;
28 import org.activeio.RequestListener;
29 import org.activeio.packet.AppendedPacket;
30 import org.activeio.packet.ByteArrayPacket;
31
32 import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap;
33 import EDU.oswego.cs.dl.util.concurrent.Executor;
34 import EDU.oswego.cs.dl.util.concurrent.Slot;
35
36
37 /***
38 * Creates a {@see org.activeio.RequestChannel} out of a {@see org.activeio.AsynchChannel}. This
39 * {@see org.activeio.RequestChannel} is thread safe and mutiplexes concurrent requests and responses over
40 * the underlying {@see org.activeio.AsynchChannel}.
41 *
42 * @version $Revision$
43 */
44 final public class AsynchChannelToConcurrentRequestChannel extends FilterAsynchChannel implements RequestChannel {
45
46 private static final byte PASSTHROUGH = 0x00;
47 private static final byte REQUEST = 0x01;
48 private static final byte RESPONSE = 0x02;
49 private static final ByteArrayPacket PASSTHROUGH_PACKET = new ByteArrayPacket(new byte[]{PASSTHROUGH});
50
51 private final ConcurrentHashMap requestMap = new ConcurrentHashMap();
52 private final Executor requestExecutor;
53 private short nextRequestId = 0;
54 private final Object writeMutex = new Object();
55
56 private RequestListener requestListener;
57
58 public AsynchChannelToConcurrentRequestChannel(AsynchChannel next) {
59 this(next, ChannelFactory.DEFAULT_EXECUTOR);
60 }
61
62 public AsynchChannelToConcurrentRequestChannel(AsynchChannel next, Executor requestExecutor) {
63 super(next);
64 this.requestExecutor=requestExecutor;
65 }
66
67 synchronized short getNextRequestId() {
68 return nextRequestId++;
69 }
70
71 /***
72 * @see org.activeio.FilterAsynchChannel#write(org.activeio.channel.Packet)
73 */
74 public void write(Packet packet) throws IOException {
75 Packet passThrough = AppendedPacket.join(PASSTHROUGH_PACKET.duplicate(), packet);
76 synchronized(writeMutex) {
77 super.write(passThrough);
78 }
79 }
80
81 /***
82 * @see org.activeio.FilterAsynchChannel#onPacket(org.activeio.channel.Packet)
83 */
84 public void onPacket(final Packet packet) {
85 switch( packet.read() ) {
86 case PASSTHROUGH:
87 super.onPacket(packet);
88 break;
89 case REQUEST:
90 try {
91 requestExecutor.execute(new Runnable(){
92 public void run() {
93 serviceRequest(packet);
94 }
95 });
96 } catch (InterruptedException e) {
97 Thread.currentThread().interrupt();
98 }
99 break;
100 case RESPONSE:
101 serviceReponse(packet);
102 break;
103 }
104 }
105
106 private void serviceRequest(Packet packet) {
107 try {
108 if( requestListener ==null )
109 throw new IOException("The RequestListener has not been set.");
110
111 PacketData data = new PacketData(packet);
112 short requestId = data.readShort();
113 Packet reponse = requestListener.onRequest(packet);
114
115
116 Packet header = createHeaderPacket(RESPONSE, requestId);
117 Packet rc = AppendedPacket.join(header, packet);
118 synchronized(writeMutex) {
119 super.write(rc);
120 }
121 } catch (IOException e) {
122 super.onPacketError(e);
123 }
124
125 }
126
127 private void serviceReponse(Packet packet) {
128
129 try {
130
131 PacketData data = new PacketData(packet);
132 short requestId = data.readShort();
133
134 Slot responseSlot = (Slot) requestMap.get(new Short(requestId));
135 responseSlot.put(packet);
136
137 } catch (IOException e) {
138 super.onPacketError(e);
139 } catch (InterruptedException e) {
140 Thread.currentThread().interrupt();
141 }
142
143
144 }
145
146 public Packet request(Packet request, long timeout) throws IOException {
147
148 Short requestId = new Short(getNextRequestId());
149 Slot responseSlot = new Slot();
150 requestMap.put(requestId, responseSlot);
151
152 Packet header = createHeaderPacket(REQUEST, requestId.shortValue());
153 Packet packet = AppendedPacket.join(header, request);
154
155 synchronized(writeMutex) {
156 super.write(packet);
157 }
158
159 try {
160
161 if( timeout == WAIT_FOREVER_TIMEOUT ) {
162 return (Packet) responseSlot.take();
163 } else if (timeout == NO_WAIT_TIMEOUT ) {
164 return (Packet) responseSlot.poll(1);
165 } else {
166 return (Packet) responseSlot.poll(timeout);
167 }
168
169 } catch (InterruptedException e) {
170 throw new InterruptedIOException(e.getMessage());
171 } finally {
172 requestMap.remove(requestId);
173 }
174 }
175
176 private Packet createHeaderPacket(byte type, short requestId) throws IOException {
177 ByteArrayPacket header = new ByteArrayPacket(new byte[]{3});
178 PacketData data = new PacketData(header);
179 data.writeByte(type);
180 data.writeShort(requestId);
181 header.flip();
182 return header;
183 }
184
185 public void setRequestListener(RequestListener requestListener) throws IOException {
186 this.requestListener = requestListener;
187 }
188
189 public RequestListener getRequestListener() {
190 return requestListener;
191 }
192 }