View Javadoc

1   /*
2    * Copyright (C) 2007 ETH Zurich
3    *
4    * This file is part of Fosstrak (www.fosstrak.org).
5    *
6    * Fosstrak is free software; you can redistribute it and/or
7    * modify it under the terms of the GNU Lesser General Public
8    * License version 2.1, as published by the Free Software Foundation.
9    *
10   * Fosstrak is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13   * Lesser General Public License for more details.
14   *
15   * You should have received a copy of the GNU Lesser General Public
16   * License along with Fosstrak; if not, write to the Free
17   * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18   * Boston, MA  02110-1301  USA
19   */
20  
21  package org.fosstrak.epcis.utils;
22  
23  import java.io.BufferedReader;
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.io.InputStreamReader;
27  import java.io.PrintWriter;
28  import java.net.ServerSocket;
29  import java.net.Socket;
30  import java.net.SocketException;
31  import java.nio.ByteBuffer;
32  import java.nio.CharBuffer;
33  import java.nio.charset.Charset;
34  import java.nio.charset.CharsetDecoder;
35  
36  /**
37   * This class implements a simple web server listening for responses from the
38   * EPCIS Query Callback interface. The server is not multi-threaded, so it will
39   * only accept one request at a time. It will only allow one instance
40   * (singleton) and will be bound to a predefined port on localhost.
41   * 
42   * @author Marco Steybe
43   */
44  public final class QueryCallbackListener extends Thread {
45  
46      private static final int PORT = 8899;
47  
48      private static QueryCallbackListener instance = null;
49  
50      private ServerSocket server = null;
51  
52      private boolean isRunning = false;
53  
54      private String response = null;
55  
56      /**
57       * Instantiates a new SubscriptionResponseListener listening on the given
58       * port.
59       * 
60       * @throws IOException
61       *             If an error setting up the communication socket occurred.
62       */
63      private QueryCallbackListener() throws IOException {
64          System.out.println("listening for query callbacks on port " + PORT + " ...");
65          server = new ServerSocket(PORT);
66      }
67  
68      /**
69       * @return The only instance of this class (singleton).
70       * @throws IOException
71       *             If an error setting up the communication socket occurred.
72       */
73      public static QueryCallbackListener getInstance() throws IOException {
74          if (instance == null) {
75              instance = new QueryCallbackListener();
76          }
77          return instance;
78      }
79  
80      /**
81       * Keeps this listener running until {@link #stopRunning()} is called.
82       * 
83       * @see java.lang.Thread#run()
84       */
85      public void run() {
86          isRunning = true;
87          while (isRunning) {
88              Socket client = null;
89              try {
90                  client = server.accept();
91                  handleConnection(client);
92              } catch (SocketException e) {
93                  // server socket closed (stopRunning was called)
94              } catch (IOException e) {
95                  e.printStackTrace();
96              } finally {
97                  if (client != null) {
98                      try {
99                          client.close();
100                     } catch (IOException e) {
101                         e.printStackTrace();
102                     }
103                 }
104             }
105         }
106     }
107 
108     /**
109      * Handles an incoming HTTP connection, reading the contents, and parsing it
110      * as XML.
111      * 
112      * @param client
113      *            The client Socket.
114      * @throws IOException
115      *             If an I/O error occurred.
116      */
117     private void handleConnection(final Socket client) throws IOException {
118         PrintWriter out = new PrintWriter(client.getOutputStream(), true);
119         InputStream is = client.getInputStream();
120         BufferedReader in = new BufferedReader(new InputStreamReader(is));
121 
122         // read content length
123         String prefix = "content-length: ";
124         String inputLine = in.readLine().toLowerCase();
125         while (!inputLine.startsWith(prefix)) {
126             // continue reading ...
127             inputLine = in.readLine().toLowerCase();
128         }
129 
130         // parse content length
131         String length = inputLine.substring(prefix.length());
132         int len = Integer.parseInt(length);
133 
134         inputLine = in.readLine();
135         while (!inputLine.equals("")) {
136             // continue reading ...
137             inputLine = in.readLine();
138         }
139 
140         // read, decode, and parse xml content (UTF-8 encoded!)
141         byte[] xml = new byte[len];
142         is.read(xml);
143         ByteBuffer buf = ByteBuffer.wrap(xml);
144         Charset charset = Charset.forName("UTF-8");
145         CharsetDecoder decoder = charset.newDecoder();
146         CharBuffer charBuffer = decoder.decode(buf);
147         parseResponse(charBuffer.toString().trim());
148 
149         // write response
150         out.write("HTTP/1.0 200 OK\n\n");
151         out.flush();
152 
153         // notify everyone waiting on us
154         synchronized (this) {
155             this.notifyAll();
156         }
157 
158         out.close();
159         in.close();
160     }
161 
162     /**
163      * Extracts the XML contents from the given String.
164      * 
165      * @param resp
166      *            The response from which the XML contents should be extracted.
167      */
168     private void parseResponse(final String resp) {
169         if (resp.startsWith("<?xml")) {
170             // remove xml declaration
171             int index = resp.indexOf("?>") + 2;
172             if (index >= 0) {
173                 response = resp.substring(index).trim();
174             }
175         }
176     }
177 
178     /**
179      * @return The received XML response.
180      */
181     public String fetchResponse() {
182         String resp = this.response;
183         this.response = null; // reset
184         return resp;
185     }
186 
187     /**
188      * @return Wheter this thread is running.
189      */
190     public boolean isRunning() {
191         return isRunning;
192     }
193 
194     /**
195      * Stops this thread from running.
196      */
197     public void stopRunning() {
198         isRunning = false;
199         instance = null;
200         try {
201             server.close();
202         } catch (IOException e) {
203             e.printStackTrace();
204         }
205     }
206 
207 }