/** * Copyright (c) 2011-2014, OpenIoT * * This file is part of OpenIoT. * * OpenIoT is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, version 3 of the License. * * OpenIoT is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with OpenIoT. If not, see <http://www.gnu.org/licenses/>. * * Contact: OpenIoT mailto: info@openiot.eu * @author Ali Salehi * @author Mehdi Riahi * @author Timotee Maret * @author Julien Eberle */ package org.openiot.gsn.http.rest; import org.openiot.gsn.DataDistributer; import org.openiot.gsn.Main; import org.openiot.gsn.Mappings; import org.openiot.gsn.beans.VSensorConfig; import org.openiot.gsn.http.ac.DataSource; import org.openiot.gsn.http.ac.GeneralServicesAPI; import org.openiot.gsn.http.ac.User; import org.openiot.gsn.storage.SQLUtils; import org.openiot.gsn.storage.SQLValidator; import org.openiot.gsn.utils.Helpers; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.StringTokenizer; import java.util.concurrent.LinkedBlockingQueue; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.bind.DatatypeConverter; import org.apache.http.HttpStatus; import org.apache.log4j.Logger; import org.eclipse.jetty.continuation.Continuation; import org.eclipse.jetty.continuation.ContinuationSupport; public class RestStreamHanlder extends HttpServlet { public static final int SUCCESS_200 = 200; private static final int _300 = 300; private static final String STREAMING = "/streaming/"; private static transient Logger logger = Logger.getLogger ( RestStreamHanlder.class ); public void doGet ( HttpServletRequest request , HttpServletResponse response ) throws ServletException{ Object is2ndPass = request.getAttribute("2ndPass"); Continuation continuation = ContinuationSupport.getContinuation(request); if(continuation.isExpired()){ logger.debug("Continuation has expired."); return; } String[] cred = null; if (Main.getContainerConfig().isAcEnabled()) { cred = parseAuthorizationHeader(request); if (cred == null) { try { response.setHeader("WWW-Authenticate", "Basic realm=\"GSN Access Control\"");// set the supported challenge response.sendError(HttpStatus.SC_UNAUTHORIZED, "Unauthorized access."); } catch (IOException e) { logger.debug(e.getMessage(), e); } return; } } if(is2ndPass == null) { continuation.setAttribute("2ndPass", Boolean.TRUE); continuation.setAttribute("status", new LinkedBlockingQueue<Boolean>(1)); continuation.setTimeout(-1); // Disable the timeout on the continuation. continuation.suspend(); final DefaultDistributionRequest streamingReq; try { URLParser parser = new URLParser(request); // if ( Main.getContainerConfig().isAcEnabled()){ String vsName = parser.getVSensorConfig().getName(); if (DataSource.isVSManaged(vsName)) { User user = GeneralServicesAPI.getInstance().doLogin(cred[0], cred[1]); if ((user == null || (! user.isAdmin() && ! user.hasReadAccessRight(vsName)))) { response.setHeader("WWW-Authenticate", "Basic realm=\"GSN Access Control\"");// set the supported challenge response.sendError(HttpStatus.SC_UNAUTHORIZED, "Unauthorized access."); return; } } } // RestDelivery deliverySystem = new RestDelivery(continuation); streamingReq = DefaultDistributionRequest.create(deliverySystem, parser.getVSensorConfig(), parser.getQuery(), parser.getStartTime()); DataDistributer.getInstance(deliverySystem.getClass()).addListener(streamingReq); }catch (Exception e) { logger.warn(e.getMessage()); continuation.complete(); } }else { boolean status = false; try{ status = !continuation.getServletResponse().getWriter().checkError(); } catch (Exception e) { logger.debug(e.getMessage(), e); } continuation.suspend(); try { ((LinkedBlockingQueue<Boolean>)continuation.getAttribute("status")).put(status); } catch (InterruptedException e) { logger.debug(e.getMessage(), e); } } } /** * This happens at the server */ public void doPost ( HttpServletRequest request , HttpServletResponse response ) throws ServletException { try { URLParser parser = new URLParser(request); String vsName = parser.getVSensorConfig().getName(); // if (Main.getContainerConfig().isAcEnabled()) { String[] cred = parseAuthorizationHeader(request); if (cred == null) { try { response.setHeader("WWW-Authenticate", "Basic realm=\"GSN Access Control\"");// set the supported challenge response.sendError(HttpStatus.SC_UNAUTHORIZED, "Unauthorized access."); } catch (IOException e) { logger.debug(e.getMessage(), e); } return; } if (DataSource.isVSManaged(vsName)){ User user = GeneralServicesAPI.getInstance().doLogin(cred[0], cred[1]); if ((user == null || (! user.isAdmin() && ! user.hasReadAccessRight(vsName)))) { response.setHeader("WWW-Authenticate", "Basic realm=\"GSN Access Control\"");// set the supported challenge response.sendError(HttpStatus.SC_UNAUTHORIZED, "Unauthorized access."); return; } } } // Double notificationId = Double.parseDouble(request.getParameter(PushDelivery.NOTIFICATION_ID_KEY)); String localContactPoint = request.getParameter(PushDelivery.LOCAL_CONTACT_POINT); if (localContactPoint == null) { logger.warn("Push streaming request received without "+PushDelivery.LOCAL_CONTACT_POINT+" parameter !"); return; } //checking to see if there is an already registered notification id, in that case, we ignore (re)registeration. DeliverySystem delivery; if (parser.pushType.equals("wp")) delivery = new WPPushDelivery(localContactPoint,notificationId,response.getWriter(),parser.nClass,parser.nMessage); else delivery = new PushDelivery(localContactPoint,notificationId,response.getWriter()); boolean isExist = DataDistributer.getInstance(delivery.getClass()).contains(delivery); if (isExist) { logger.debug("Keep alive request received for the notification-id:"+notificationId); response.setStatus(SUCCESS_200); delivery.close(); return; } DefaultDistributionRequest distributionReq = DefaultDistributionRequest.create(delivery, parser.getVSensorConfig(), parser.getQuery(), parser.getStartTime()); logger.debug("Rest request received: "+distributionReq.toString()); DataDistributer.getInstance(delivery.getClass()).addListener(distributionReq); logger.debug("Streaming request received and registered:"+distributionReq.toString()); }catch (Exception e) { logger.warn(e.getMessage(),e); return ; } } /** * This happens at the client */ public void doPut( HttpServletRequest request , HttpServletResponse response ) throws ServletException { double notificationId = Double.parseDouble(request.getParameter(PushDelivery.NOTIFICATION_ID_KEY)); PushRemoteWrapper notification = NotificationRegistry.getInstance().getNotification(notificationId); try { if (notification!=null) { boolean status = notification.manualDataInsertion(request.getParameter(PushDelivery.DATA)); if (status) response.setStatus(SUCCESS_200); else response.setStatus(_300); }else { logger.warn("Received a Http put request for an INVALID notificationId: " + notificationId); response.sendError(_300); } } catch (IOException e) { logger.warn("Failed in writing the status code into the connection.\n"+e.getMessage(),e); } } /** * * @param request * @return [username,password] or null if unable to retrieve these pieces of information. */ private String[] parseAuthorizationHeader(HttpServletRequest request) { // Get username/password from the Authorization header String authHeader = request.getHeader("Authorization"); // form: BASIC d2VibWFzdGVyOnRyeTJndWVTUw if (authHeader != null) { String[] ahs = authHeader.split(" "); if (ahs.length == 2) { String b64UsernamPassword = ahs[1]; // we get: d2VibWFzdGVyOnRyeTJndWVTUw String userPass = new String(DatatypeConverter.parseBase64Binary(b64UsernamPassword)); // form: username:passsword String[] ups; if ((ups = userPass.split(":")).length == 2) { return new String[]{ ups[0], // username ups[1] // password }; } } } return null; } class URLParser{ private String query,tableName,pushType,nMessage; private int nClass; private long startTime; private VSensorConfig config; public URLParser(HttpServletRequest request) throws UnsupportedEncodingException, Exception { String requestURI = request.getRequestURI().substring(request.getRequestURI().toLowerCase().indexOf(STREAMING)+STREAMING.length()); StringTokenizer tokens = new StringTokenizer(requestURI,"/"); startTime = System.currentTimeMillis(); String first = tokens.nextToken(); if (first.startsWith("wp")){ //registering from a Windows Phone pushType = "wp"; nClass = Integer.parseInt(first.substring(2,3)); nMessage = URLDecoder.decode(first.substring(3),"UTF-8"); query = tokens.nextToken(); }else{ query = first; } query = URLDecoder.decode(query,"UTF-8"); if (tokens.hasMoreTokens()) startTime= Helpers.convertTimeFromIsoToLong(URLDecoder.decode(tokens.nextToken(),"UTF-8")); tableName = SQLValidator.getInstance().validateQuery(query); if (tableName==null) throw new RuntimeException("Bad Table name in the query:"+query); /** IMPORTANT: We change the table names to lower-case as some databases (e.g., MySQL on linux) are case sensitive and in * general we use lower case for table names in GSN. **/ tableName=tableName.trim(); query = SQLUtils.newRewrite(query, tableName, tableName.toLowerCase()).toString(); tableName=tableName.toLowerCase(); config = Mappings.getConfig(tableName); } public VSensorConfig getVSensorConfig() { return config; } public String getQuery() { return query; } public long getStartTime() { return startTime; } } }