Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
ByteProtocol |
|
| 3.25;3.25 |
1 | /* | |
2 | * $Id: ByteProtocol.java 7976 2007-08-21 14:26:13Z dirk.olmes $ | |
3 | * -------------------------------------------------------------------------------------- | |
4 | * Copyright (c) MuleSource, Inc. All rights reserved. http://www.mulesource.com | |
5 | * | |
6 | * The software in this package is published under the terms of the CPAL v1.0 | |
7 | * license, a copy of which has been included with this distribution in the | |
8 | * LICENSE.txt file. | |
9 | */ | |
10 | ||
11 | package org.mule.providers.tcp.protocols; | |
12 | ||
13 | import org.mule.providers.tcp.TcpProtocol; | |
14 | import org.mule.umo.provider.UMOMessageAdapter; | |
15 | import org.mule.umo.provider.UMOStreamMessageAdapter; | |
16 | import org.mule.util.ClassUtils; | |
17 | import org.mule.util.IOUtils; | |
18 | ||
19 | import java.io.IOException; | |
20 | import java.io.InputStream; | |
21 | import java.io.OutputStream; | |
22 | import java.io.Serializable; | |
23 | import java.net.SocketException; | |
24 | import java.net.SocketTimeoutException; | |
25 | ||
26 | import org.apache.commons.lang.SerializationUtils; | |
27 | import org.apache.commons.logging.Log; | |
28 | import org.apache.commons.logging.LogFactory; | |
29 | ||
30 | /** | |
31 | * This Abstract class has been introduced so as to have the byte protocols (i.e. the | |
32 | * protocols that had only a single write method taking just an array of bytes as a | |
33 | * parameter) to inherit from since they will all behave the same, i.e. if the object | |
34 | * is serializable, serialize it into an array of bytes and send it. | |
35 | * | |
36 | * <p>Note that the raw write method has changed name from <code>write</code> to | |
37 | * <code>writeByteArray</code>. This is to remove ambiguity from the code. In almost | |
38 | * all cases it is possible to call {@link #write(java.io.OutputStream, Object)} which | |
39 | * will, via {@link #write(java.io.OutputStream, Object)}, dispatch to | |
40 | * {@link #writeByteArray(java.io.OutputStream, byte[])}.</p>. | |
41 | */ | |
42 | public abstract class ByteProtocol implements TcpProtocol | |
43 | { | |
44 | ||
45 | 0 | private static final Log logger = LogFactory.getLog(DefaultProtocol.class); |
46 | private static final long PAUSE_PERIOD = 100; | |
47 | public static final int EOF = -1; | |
48 | ||
49 | // make this really clear in subclasses, because otherwise people will forget | |
50 | public static final boolean STREAM_OK = true; | |
51 | public static final boolean NO_STREAM = false; | |
52 | private boolean streamOk; | |
53 | ||
54 | public ByteProtocol(boolean streamOk) | |
55 | 0 | { |
56 | 0 | this.streamOk = streamOk; |
57 | 0 | } |
58 | ||
59 | public void write(OutputStream os, Object data) throws IOException | |
60 | { | |
61 | 0 | if (data instanceof UMOStreamMessageAdapter) |
62 | { | |
63 | 0 | if (streamOk) |
64 | { | |
65 | 0 | IOUtils.copy(((UMOStreamMessageAdapter) data).getInputStream(), os); |
66 | 0 | os.flush(); |
67 | 0 | os.close(); |
68 | } | |
69 | else | |
70 | { | |
71 | 0 | throw new IOException("TCP protocol " + ClassUtils.getSimpleName(getClass()) |
72 | + " cannot handle streaming"); | |
73 | } | |
74 | } | |
75 | 0 | else if (data instanceof UMOMessageAdapter) |
76 | { | |
77 | 0 | write(os, ((UMOMessageAdapter) data).getPayload()); |
78 | } | |
79 | 0 | else if (data instanceof byte[]) |
80 | { | |
81 | 0 | writeByteArray(os, (byte[]) data); |
82 | } | |
83 | 0 | else if (data instanceof String) |
84 | { | |
85 | // TODO SF: encoding is lost/ignored; it is probably a good idea to have | |
86 | // a separate "stringEncoding" property on the protocol | |
87 | 0 | writeByteArray(os, ((String) data).getBytes()); |
88 | } | |
89 | 0 | else if (data instanceof Serializable) |
90 | { | |
91 | 0 | writeByteArray(os, SerializationUtils.serialize((Serializable) data)); |
92 | } | |
93 | else | |
94 | { | |
95 | 0 | throw new IllegalArgumentException("Cannot serialize data: " + data); |
96 | } | |
97 | 0 | } |
98 | ||
99 | protected void writeByteArray(OutputStream os, byte[] data) throws IOException | |
100 | { | |
101 | 0 | os.write(data); |
102 | 0 | } |
103 | ||
104 | /** | |
105 | * Manage non-blocking reads and handle errors | |
106 | * | |
107 | * @param is The input stream to read from | |
108 | * @param buffer The buffer to read into | |
109 | * @return The amount of data read (always non-zero, -1 on EOF or socket exception) | |
110 | * @throws IOException other than socket exceptions | |
111 | */ | |
112 | protected int safeRead(InputStream is, byte[] buffer) throws IOException | |
113 | { | |
114 | 0 | return safeRead(is, buffer, buffer.length); |
115 | } | |
116 | ||
117 | /** | |
118 | * Manage non-blocking reads and handle errors | |
119 | * | |
120 | * @param is The input stream to read from | |
121 | * @param buffer The buffer to read into | |
122 | * @param size The amount of data (upper bound) to read | |
123 | * @return The amount of data read (always non-zero, -1 on EOF or socket exception) | |
124 | * @throws IOException other than socket exceptions | |
125 | */ | |
126 | protected int safeRead(InputStream is, byte[] buffer, int size) throws IOException | |
127 | { | |
128 | int len; | |
129 | try | |
130 | { | |
131 | do | |
132 | { | |
133 | 0 | len = is.read(buffer, 0, size); |
134 | 0 | if (0 == len) |
135 | { | |
136 | // wait for non-blocking input stream | |
137 | // use new lock since not expecting notification | |
138 | try | |
139 | { | |
140 | 0 | Thread.sleep(PAUSE_PERIOD); |
141 | } | |
142 | 0 | catch (InterruptedException e) |
143 | { | |
144 | // no-op | |
145 | 0 | } |
146 | } | |
147 | } | |
148 | 0 | while (0 == len); |
149 | 0 | return len; |
150 | } | |
151 | 0 | catch (SocketException e) |
152 | { | |
153 | // do not pollute the log with a stacktrace, log only the message | |
154 | 0 | logger.info("Socket exception occured: " + e.getMessage()); |
155 | 0 | return EOF; |
156 | } | |
157 | 0 | catch (SocketTimeoutException e) |
158 | { | |
159 | 0 | logger.debug("Socket timeout."); |
160 | 0 | return EOF; |
161 | } | |
162 | } | |
163 | ||
164 | /** | |
165 | * Make a single transfer from source to dest via a byte array buffer | |
166 | * | |
167 | * @param source Source of data | |
168 | * @param buffer Buffer array for transfer | |
169 | * @param dest Destination of data | |
170 | * @return Amount of data transferred, or -1 on eof or socket error | |
171 | * @throws IOException On non-socket error | |
172 | */ | |
173 | protected int copy(InputStream source, byte[] buffer, OutputStream dest) throws IOException | |
174 | { | |
175 | 0 | return copy(source, buffer, dest, buffer.length); |
176 | } | |
177 | ||
178 | /** | |
179 | * Make a single transfer from source to dest via a byte array buffer | |
180 | * | |
181 | * @param source Source of data | |
182 | * @param buffer Buffer array for transfer | |
183 | * @param dest Destination of data | |
184 | * @param size The amount of data (upper bound) to read | |
185 | * @return Amount of data transferred, or -1 on eof or socket error | |
186 | * @throws IOException On non-socket error | |
187 | */ | |
188 | protected int copy(InputStream source, byte[] buffer, OutputStream dest, int size) throws IOException | |
189 | { | |
190 | 0 | int len = safeRead(source, buffer, size); |
191 | 0 | if (len > 0) |
192 | { | |
193 | 0 | dest.write(buffer, 0, len); |
194 | } | |
195 | 0 | return len; |
196 | } | |
197 | ||
198 | protected byte[] nullEmptyArray(byte[] data) | |
199 | { | |
200 | 0 | if (0 == data.length) |
201 | { | |
202 | 0 | return null; |
203 | } | |
204 | else | |
205 | { | |
206 | 0 | return data; |
207 | } | |
208 | } | |
209 | ||
210 | } |