TurtleDB
A mini distributed database system
|
00001 /*------------------------------------------------------------------------- 00002 Simple distributed database engine 00003 Copyright (C) 2012 Sylvain Hallé 00004 00005 This program is free software: you can redistribute it and/or modify 00006 it under the terms of the GNU General Public License as published by 00007 the Free Software Foundation, either version 3 of the License, or 00008 (at your option) any later version. 00009 00010 This program is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 GNU General Public License for more details. 00014 00015 You should have received a copy of the GNU General Public License 00016 along with this program. If not, see <http://www.gnu.org/licenses/>. 00017 -------------------------------------------------------------------------*/ 00018 package ca.uqac.dim.turtledb; 00019 00020 import java.io.*; 00021 import java.net.*; 00022 import java.util.*; 00023 00024 public class HttpCommunicator extends Communicator 00025 { 00026 00027 protected Map<String,SiteInfo> m_siteInfo; 00028 00029 protected Engine m_engine; 00030 00031 public HttpCommunicator() 00032 { 00033 super(); 00034 m_siteInfo = new HashMap<String,SiteInfo>(); 00035 } 00036 00037 public void addSiteInfo(String name, String url) 00038 { 00039 SiteInfo si = new SiteInfo(name, url); 00040 m_siteInfo.put(name, si); 00041 } 00042 00043 protected void sendQuery(String site_name, Relation r) throws Communicator.QueryExecutionException 00044 { 00045 SiteInfo si = m_siteInfo.get(site_name); 00046 if (si == null) 00047 { 00048 throw new Communicator.QueryExecutionException("Unknown site: " + site_name); 00049 } 00050 String soap_string = XmlQueryFormatter.toXmlString(r); 00051 @SuppressWarnings("unused") 00052 String return_value = ""; 00053 try 00054 { 00055 return_value = postData(si.m_siteUrl, soap_string); 00056 } 00057 catch (IOException e) 00058 { 00059 throw new Communicator.QueryExecutionException("IOException while sending data to site " + site_name); 00060 } 00061 } 00062 00063 protected void sendQuery(String site_name, Set<Relation> rels) throws Communicator.QueryExecutionException 00064 { 00065 for (Relation r : rels) 00066 sendQuery(site_name, r); 00067 } 00068 00076 protected String sendData(URL url, String data) throws IOException 00077 { 00078 URLConnection conn = url.openConnection(); 00079 conn.setDoOutput(true); 00080 OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream()); 00081 wr.write(data); 00082 wr.flush(); 00083 00084 // Get the response 00085 BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream())); 00086 String line; 00087 StringBuilder out = new StringBuilder(); 00088 while ((line = rd.readLine()) != null) 00089 { 00090 out.append(line).append("\n"); 00091 } 00092 wr.close(); 00093 rd.close(); 00094 return out.toString(); 00095 } 00096 00106 protected String postData(String destination_url, String payload) throws IOException 00107 { 00108 URL url = new URL(destination_url); 00109 StringBuilder out = new StringBuilder(); 00110 out.append("POST ").append(url.getFile()).append(" HTTP/1.1\n"); 00111 out.append("Host: ").append(url.getHost()).append("\n"); 00112 out.append("User-Agent: TurtleDB\n"); 00113 out.append("Content-Type: application/xml\n"); 00114 out.append("Content-Length: ").append(payload.length()).append("\n"); 00115 out.append(payload); 00116 String http_response = sendData(url, out.toString()); 00117 if (!http_response.startsWith("200 OK")) 00118 throw new IOException("HTTP error code"); 00119 return http_response; 00120 } 00121 00126 protected class SiteInfo 00127 { 00128 public String m_siteName; 00129 public String m_siteUrl; 00130 00131 public SiteInfo(String name, String url) 00132 { 00133 super(); 00134 m_siteName = name; 00135 m_siteUrl = url; 00136 } 00137 } 00138 00139 @Override 00140 public QueryProcessor getQueryProcessor(Relation query) 00141 { 00142 QueryPlan qp = m_engine.getQueryPlan(query); 00143 return getQueryProcessor(qp); 00144 } 00145 00146 @Override 00147 public QueryProcessor getQueryProcessor(QueryPlan qp) 00148 { 00149 return new HttpQueryProcessor(qp); 00150 } 00151 00152 protected class HttpQueryProcessor extends QueryProcessor 00153 { 00154 protected QueryPlan m_queryPlan; 00155 00156 protected Relation m_result; 00157 00158 public HttpQueryProcessor(QueryPlan qp) 00159 { 00160 m_queryPlan = qp; 00161 } 00162 00163 @Override 00164 public void run() 00165 { 00166 // Dispatch query plan pieces to every site 00167 for (String site_name : m_queryPlan.keySet()) 00168 { 00169 Set<Relation> rels = m_queryPlan.get(site_name); 00170 try 00171 { 00172 sendQuery(site_name, rels); 00173 } 00174 catch (Communicator.QueryExecutionException e) 00175 { 00176 e.printStackTrace(); 00177 } 00178 } 00179 } 00180 00181 @Override 00182 public Relation getResult() 00183 { 00184 return m_result; 00185 } 00186 } 00187 00191 @Override 00192 public void run() 00193 { 00194 int port = 1234; 00195 ServerSocket serversocket = null; 00196 try 00197 { 00198 serversocket = new ServerSocket(port); 00199 } 00200 catch (IOException e) 00201 { 00202 // Nothing to do if we can't open a socket 00203 return; 00204 } 00205 00206 //go in a infinite loop, wait for connections, process request, send response 00207 while (true) 00208 { 00209 try 00210 { 00211 //this call waits/blocks until someone connects to the port we 00212 //are listening to 00213 Socket connectionsocket = serversocket.accept(); 00214 //Read the http request from the client from the socket interface 00215 //into a buffer. 00216 BufferedReader input = 00217 new BufferedReader(new InputStreamReader(connectionsocket. 00218 getInputStream())); 00219 //Prepare a outputstream from us to the client, 00220 //this will be used sending back our response 00221 //(header + requested file) to the client. 00222 DataOutputStream output = 00223 new DataOutputStream(connectionsocket.getOutputStream()); 00224 http_handler(input, output); 00225 } 00226 catch (Exception e) 00227 { 00228 e.printStackTrace(); 00229 } 00230 } 00231 } 00232 00233 //this method makes the HTTP header for the response 00234 //the headers job is to tell the browser the result of the request 00235 //among if it was successful or not. 00236 private String construct_http_header(int return_code) 00237 { 00238 String s = "HTTP/1.1 "; 00239 switch (return_code) 00240 { 00241 case 200: 00242 s = s + "200 OK"; 00243 break; 00244 case 400: 00245 s = s + "400 Bad Request"; 00246 break; 00247 case 403: 00248 s = s + "403 Forbidden"; 00249 break; 00250 case 404: 00251 s = s + "404 Not Found"; 00252 break; 00253 case 500: 00254 s = s + "500 Internal Server Error"; 00255 break; 00256 case 501: 00257 s = s + "501 Not Implemented"; 00258 break; 00259 } 00260 s = s + "\r\n"; 00261 s = s + "Connection: close\r\n"; //we can't handle persistent connections 00262 s = s + "Server: TurtleDB\r\n"; //server name 00263 s = s + "\r\n"; //this marks the end of the httpheader 00264 return s; 00265 } 00266 00267 private void http_handler(BufferedReader input, DataOutputStream output) 00268 { 00269 int method = 0; //1 post, 0 not supported 00270 try 00271 { 00272 String tmp = input.readLine(); //read from the stream 00273 if (tmp.startsWith("POST")) 00274 { //compare it is it GET 00275 method = 1; 00276 } //if we set it to method 1 00277 if (method == 0) 00278 { // not supported 00279 output.writeBytes(construct_http_header(501)); 00280 output.close(); 00281 return; 00282 } 00283 String line = input.readLine(); 00284 while (line != null) 00285 { 00286 00287 line = input.readLine(); 00288 } 00289 } 00290 catch (IOException e) 00291 { 00292 e.printStackTrace(); 00293 } 00294 } 00295 00296 }