TurtleDB
A mini distributed database system
src/ca/uqac/dim/turtledb/HttpCommunicator.java
Go to the documentation of this file.
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 }