1 /**
2 * $Id: ServerSocketLineReaderConsumer.java 55 2006-11-08 14:06:59Z maldito_orco $
3 * $Revision: 55 $
4 * $Date: 2006-11-08 11:06:59 -0300 (Wed, 08 Nov 2006) $
5 *
6 * =========================================================================
7 *
8 * Copyright 2005 Tubo
9 *
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
13 *
14 * http://www.apache.org/licenses/LICENSE-2.0
15 *
16 * Unless required by applicable law or agreed to in writing, software
17 * distributed under the License is distributed on an "AS IS" BASIS,
18 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 * See the License for the specific language governing permissions and
20 * limitations under the License.
21 */
22 package org.tubo.resource.consumer.serversocketlinereader;
23
24
25 import org.apache.commons.logging.Log;
26 import org.apache.commons.logging.LogFactory;
27
28 import org.tubo.resource.consumer.serversocket.ServerSocketConsumer;
29 import org.tubo.resource.flow.FlowContext;
30 import org.tubo.exception.TuboConsumerException;
31 import org.tubo.item.Item;
32
33 import java.io.InputStream;
34 import java.io.OutputStream;
35 import java.io.PrintWriter;
36 import java.io.BufferedReader;
37 import java.io.InputStreamReader;
38 import java.io.IOException;
39
40 import java.net.Socket;
41
42 import java.util.Map;
43 import java.util.StringTokenizer;
44 import java.util.HashMap;
45
46 /**
47 * <p>
48 * This class is a Consumer Component based on ServerSocket.
49 * When the connection is established on ServerSocketConsumerWorker this class
50 * call process(Socket) on ServerSocketConsumer which is overwited by this
51 * class.
52 * When a process(Socket) is call, streams are opened and delegate procesing
53 * to process(InputStream,OutputStream). In this method tree event are throw:
54 * <li>
55 * <ul>NEW_CONNECTION_EVENT: which can be used to sey hello :)</ul>
56 * <ul>READ_LINE_EVENT: which is throw when a new line of text is read</ul>
57 * <ul>GOODBYE_EVENT: which is throw when the text read matches with 'goodbye-words' property</ul>
58 * </li>
59 * </p>
60 * <p>
61 * The method process(Socket) is override by this class,
62 * </p>
63 *
64 * <p>
65 * Created: Oct 19, 2006, 6:12:44 PM<br>
66 * Last Modification Date: $Date: 2006-11-08 11:06:59 -0300 (Wed, 08 Nov 2006) $
67 * </p>
68 *
69 * @author maldito_orco (maldito_orco@users.sourceforge.net)
70 * @version $Revision: 55 $
71 */
72 public class ServerSocketLineReaderConsumer extends ServerSocketConsumer {
73 public static final String RCS_ID = "$Id: ServerSocketLineReaderConsumer.java 55 2006-11-08 14:06:59Z maldito_orco $";
74 private static Log log = LogFactory.getLog(ServerSocketLineReaderConsumer.class);
75
76 public static final String SERVERSOCKET_LINE_READER_INPUT_PROPERTY = "SSLINEREADER_INPUT";
77 public static final String SERVERSOCKET_LINE_READER_OUTPUT_PROPERTY = "SSLINEREADER_OUTPUT";
78
79 public static final String READ_LINE_EVENT = "READ_LINE_EVENT";
80 public static final String GOODBYE_EVENT = "GOODBYE_EVENT";
81
82 /** Singleton item for this consumer thread */
83 private Item item = null;
84 private PrintWriter out = null;
85 private BufferedReader in = null;
86
87 private Map goodbyeWords = null;
88
89 /**
90 * <p>
91 * Override normal ServerSocketConsumer behavior. On this implementation
92 * obtains in and out streams. Delegate the stream procesing on other
93 * method.
94 * </p>
95 *
96 * @param socket A new socket open from client
97 * @throws TuboConsumerException When geting InputStream or OutputStream from socket
98 */
99 public void process(Socket socket) throws TuboConsumerException {
100 InputStream is = null;
101 OutputStream os = null;
102 try {
103
104
105 is = socket.getInputStream();
106
107
108 os = socket.getOutputStream();
109
110
111 process(is,os);
112 } catch (IOException e) {
113 throw getResourceManager().getExceptionManager().getException(3510,e);
114 } finally {
115 if (os!=null)
116 try {
117
118
119 os.flush();
120 os.close();
121 } catch (IOException e) {
122 log.info("Error closing OutputStream",e);
123 }
124 if (is!=null)
125 try {
126
127
128 is.close();
129 } catch (IOException e) {
130 log.info("Error closing InputStream",e);
131 }
132 }
133 }
134
135
136 /**
137 * <p>
138 * Streams Procesing.
139 * </p>
140 *
141 * @param is Socket input
142 * @param os Socket output
143 * @throws TuboConsumerException
144 */
145 public void process(InputStream is, OutputStream os) throws TuboConsumerException {
146
147
148 if (goodbyeWords==null)
149 loadGoodbyeWords();
150
151
152 try {
153
154
155 in = new BufferedReader(new InputStreamReader(is));
156
157
158 out = new PrintWriter(os, true);
159
160
161
162 doConsumerLifeCycle(NEW_CONNECTION_EVENT,null);
163
164
165 String inputLine;
166 boolean goodbye =false;
167 while ((!goodbye) && ((inputLine = in.readLine()) != null)) {
168
169
170 if (goodbyeWords.containsKey(inputLine)) {
171 goodbye=true;
172 doConsumerLifeCycle(GOODBYE_EVENT, inputLine);
173 } else
174 doConsumerLifeCycle(READ_LINE_EVENT, inputLine);
175 }
176 } catch (IOException e) {
177 throw getResourceManager().getExceptionManager().getException(3511,e);
178 } finally {
179 if (out!=null)
180 try {
181 out.flush();
182 } catch (Exception e) {
183 log.info("Error flushing OutputStream",e);
184 }
185 }
186 }
187
188
189 /**
190 * Override this method to create only one item.
191 * To create the item the super method is used.
192 *
193 * @return The item
194 * TODO: move this behavior to BaseConsumerImpl...
195 */
196 protected Item createItem() {
197 if (item == null)
198 item = super.createItem();
199 return item;
200
201 }
202
203 /**
204 * Hook from consumer life cycle called before listener execution.
205 * This method have the responsability of set input line like a
206 * item property.
207 *
208 * @param flowContext The flowContext
209 */
210 protected void preOriginFlowEvent(FlowContext flowContext) {
211 if (!NEW_CONNECTION_EVENT.equals(flowContext.getOriginEvent().getAction())) {
212 String inputLine = (String)flowContext.getItem().getConsumedRawData();
213 if (inputLine!=null)
214 item.setProperty(SERVERSOCKET_LINE_READER_INPUT_PROPERTY,inputLine);
215 }
216 }
217
218
219 /**
220 * Hook from consumer life cycle called after listener execution.
221 *
222 * @param flowContext The item
223 */
224 protected void postOriginFlowEvent(FlowContext flowContext) {
225
226
227 String outputLine = (String)flowContext.getItem().getProperty(SERVERSOCKET_LINE_READER_OUTPUT_PROPERTY);
228
229
230 if (outputLine != null)
231 out.println(outputLine);
232
233
234 }
235
236 /**
237 * Load goodbyeWords Map. This words are cut condition for read/write cycle.
238 *
239 * @throws TuboConsumerException This exception is throw if property 'goodbye-words' is not configured or with a wrong format.
240 */
241 private void loadGoodbyeWords() throws TuboConsumerException {
242 goodbyeWords = new HashMap();
243
244
245 String goodbyeWordsString = super.getProperty("goodbye-words");
246
247
248 if (goodbyeWordsString==null || "".equals(goodbyeWordsString.trim()))
249 throw getResourceManager().getExceptionManager().getException(3512);
250
251
252 StringTokenizer tokenizer = new StringTokenizer(goodbyeWordsString,",",false);
253
254
255 if (tokenizer.countTokens()==0)
256 throw getResourceManager().getExceptionManager().getException(3513,goodbyeWordsString);
257
258
259 while (tokenizer.hasMoreElements()) {
260
261
262 String word = tokenizer.nextToken();
263
264
265 goodbyeWords.put(word,word);
266 }
267 }
268 }