/* * SMART FP7 - Search engine for MultimediA enviRonment generated contenT * Webpage: http://smartfp7.eu * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * The Original Code is Copyright (c) 2012-2013 Athens Information Technology * All Rights Reserved * * Contributor: * Nikolaos Katsarakis nkat@ait.edu.gr * Menelaos Bakopoulos mbak@ait.edu.gr */ package eu.smartfp7.EdgeNode; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.PrintWriter; import java.net.MalformedURLException; import java.util.List; import java.util.Properties; import java.util.UUID; import javax.servlet.Servlet; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.transform.Source; import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; import org.lightcouch.CouchDbClient; import org.lightcouch.Response; import org.xml.sax.SAXException; import eu.smartfp7.utils.Json; import eu.smartfp7.utils.Xml; /** * Servlet implementation class CreateFeed */ @WebServlet("/createFeed") public class CreateFeed extends HttpServlet { private static final long serialVersionUID = 1L; // Members for XML parsing and validation private Validator validator = null; // CouchDB access parameters private CouchDbClient dbClient = null; private String server, user, pass; private int port; /** * @see HttpServlet#HttpServlet() */ public CreateFeed() { super(); } /** * @see Servlet#init(ServletConfig) */ public void init(ServletConfig config) { try { super.init(config); } catch (ServletException e) { System.err.println("Can not initialize servlet"); return; } // Read couchdb properties from file and initialise the client for feeds Properties dbProps = new Properties(); try { dbProps.load(getServletContext().getResourceAsStream("/WEB-INF/couchdb.properties")); } catch (IOException e1) { System.err.println("Can not open couchdb properties file"); return; } server = dbProps.getProperty("server"); port = Integer.parseInt(dbProps.getProperty("port")); user = dbProps.getProperty("user"); pass = dbProps.getProperty("pass"); // Test for incorrect user/pass or CouchDB server offline try { dbClient = new CouchDbClient("feeds", true, "http", server, port, user, pass); } catch (Exception e) { System.out .println("Could not connect to CouchDB, check that the server is running and that the correct username/pass is set"); System.out.println("Current configuration: server=" + server + ", port=" + port + ", user= " + user + ", pass= " + pass); return; } // Initialise the XSD validator SchemaFactory factory = null; Schema schema = null; // 1. Lookup a factory for the W3C XML Schema language factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema"); if (factory == null) { System.err.println("Cannot initialize schema factory"); return; } // 2. Compile the schema. try { schema = factory.newSchema(getServletContext().getResource("/SMART_Datafeed_Schema_v0.3.xsd")); } catch (SAXException e) { System.err.println("Cannot read XSD file"); } catch (MalformedURLException e) { System.err.println("Invalid URL"); } catch (NullPointerException npe) { System.err.println("Could not find required xsd file"); } if (schema != null) { // 3. Get a validator from the schema. validator = schema.newValidator(); } } /** * @see Servlet#destroy() */ public void destroy() { if (dbClient != null) dbClient.shutdown(); } /** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) { try { response.sendRedirect("createFeed.html"); } catch (IOException e1) { System.out.println("doGet IOException: Can not redirect"); return; } } /** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) { PrintWriter out = null; response.setContentType("application/xml;charset=UTF-8"); System.out.println("CreateFeedPost called"); try { out = response.getWriter(); } catch (IOException e1) { System.err.println("Can not open response writer"); response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return; } if (dbClient == null) { response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); out.write("<xml><status>Error</status><description>Could not connect to CouchDB, check that the server is running and that the correct username/pass is set</description></xml>"); return; } if (validator == null) { response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); out.write("<xml><status>Error</status><description>Could load xml validator</description></xml>"); return; } // Store the request to a StringBuilder in order to reuse it StringBuilder reqStrBuilder = new StringBuilder(); try { BufferedReader reader = request.getReader(); char[] buf = new char[4 * 1024]; // 4 KB char buffer int len; while ((len = reader.read(buf, 0, buf.length)) != -1) { reqStrBuilder.append(buf, 0, len); // System.out.println("Total read thus far:"+reqStrBuilder.toString()); } } catch (IOException e1) { System.err.println("Can not read input, received data:" + reqStrBuilder.toString()); response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); out.write("<xml><status>Error</status><description>Can not read input</description></xml>"); return; } String reqString = reqStrBuilder.toString(); if (request.getContentType() != null && !request.getContentType().contains("application/xml")) { response.setStatus(HttpServletResponse.SC_BAD_REQUEST); out.write("<xml><status>Error</status><description>Invalid Content-Type \"" + request.getContentType() + "\" in HTTP Header, should be \"application/xml\"</description></xml>"); return; } if (reqString.length() == 0) { response.setStatus(HttpServletResponse.SC_BAD_REQUEST); out.write("<xml><status>Error</status><description>Empty body on POST request</description></xml>"); return; } try { // 4. Parse the document you want to check, after converting to InputStream Source source = new StreamSource(new ByteArrayInputStream(reqString.getBytes())); // 5. Check the document validator.validate(source); // out.write("<xml><status>OK</status><description>Xml is valid</description></xml>"); } catch (SAXException ex) { response.setStatus(HttpServletResponse.SC_BAD_REQUEST); out.write("<xml><status>Error</status><description>Xml is not valid because :"); out.write(ex.getMessage() + "</description></xml>"); return; } catch (IOException e) { response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); out.write("<xml><status>Error</status><description>Could not read document.</description></xml>"); return; } // Convert XML to JSON and input into DB String JSON = Xml.convertToJson(reqString); if ("error".equals(JSON)) { response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); out.write("<xml><status>Error</status><description>Could not convert XML to JSON document.</description></xml>"); return; } String feed_id; String id = Json.getSimpleTextKey(JSON, "Id"); if (id == null) { response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); out.write("<xml><status>Error</status><description>Could not read \"id\" from json data " + JSON + "</description></xml>"); return; } System.out.println("Document ID from XML = " + id); feed_id = Common.cleanString(id); // Check existing feeds in EdgeNode and find an unused name // If there are multiple feeds with the same name, give a random uuid List<String> DBs = dbClient.context().getAllDbs(); boolean existing = DBs.contains(feed_id); if (existing) { for (int i = 1; i < 10; i++) { String feed_id2 = feed_id + i; if (!DBs.contains(feed_id2)) { feed_id = feed_id2; existing = false; break; } } if (existing) feed_id = UUID.randomUUID().toString().replace("-", ""); } // Replace the feed ID with the created one JSON = Json.replaceSimpleTextKey(JSON, "Id", feed_id); // Also add it into the special _id field for couchDB JSON = Json.addKeyValue(JSON, "\"_id\" : \"" + feed_id + "\""); // Add the document to feeds database Response resp = dbClient.saveJsonText(JSON); feed_id = resp.getId(); System.out.println("Created document ID = " + feed_id); // Create a database with this feed ID dbClient.context().createDB(feed_id); // Access the new database to add design document for view String viewDoc = "{\n" + "\t\"_id\": \"_design/get_data\",\n" + "\t\"language\": \"javascript\",\n" + "\t\"views\": {\n" + "\t\t \"by_date\": {\n" + "\t\t\t \"map\": \"function(doc) {\\nif(doc.timestamp && doc.data) {\\nemit(doc.timestamp, doc.data);\\n}\\n}\"\n" + "\t\t }\n" + "\t}\n" + "}\n"; try { CouchDbClient dbClientFeed = new CouchDbClient(feed_id, false, "http", server, port, user, pass); resp = dbClientFeed.saveJsonText(viewDoc); System.out.println("Created view document with ID = " + resp.getId()); dbClientFeed.shutdown(); // Write the response along with the proper status code response.setStatus(HttpServletResponse.SC_CREATED); out.write("<xml><status>OK</status><feed_id>" + feed_id + "</feed_id></xml>"); } catch (Exception e) { response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); out.write("<xml><status>Error</status><description>Could not connect to CouchDB, check that the server is running and that the correct username/pass is set</description></xml>"); } } }