/* * * Copyright (C) 2012-2014 R T Huitema. All Rights Reserved. * Web: www.42.co.nz * Email: robert@42.co.nz * Author: R T Huitema * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package nz.co.fortytwo.signalk.processor; import static nz.co.fortytwo.signalk.util.SignalKConstants.CONTEXT; import static nz.co.fortytwo.signalk.util.SignalKConstants.FORMAT; import static nz.co.fortytwo.signalk.util.SignalKConstants.FORMAT_DELTA; import static nz.co.fortytwo.signalk.util.SignalKConstants.MIN_PERIOD; import static nz.co.fortytwo.signalk.util.SignalKConstants.PATH; import static nz.co.fortytwo.signalk.util.SignalKConstants.PERIOD; import static nz.co.fortytwo.signalk.util.SignalKConstants.POLICY; import static nz.co.fortytwo.signalk.util.SignalKConstants.POLICY_FIXED; import static nz.co.fortytwo.signalk.util.SignalKConstants.SUBSCRIBE; import static nz.co.fortytwo.signalk.util.SignalKConstants.UNSUBSCRIBE; import static nz.co.fortytwo.signalk.util.SignalKConstants.vessels; import java.util.Map; import mjson.Json; import nz.co.fortytwo.signalk.server.Subscription; import nz.co.fortytwo.signalk.server.SubscriptionManagerFactory; import nz.co.fortytwo.signalk.util.ConfigConstants; import org.apache.camel.Exchange; import org.apache.camel.Processor; import org.apache.camel.component.websocket.WebsocketConstants; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; /** * Updates the subscription manager with this session subs * * @author robert * */ public class JsonSubscribeProcessor extends SignalkProcessor implements Processor{ private static Logger logger = LogManager.getLogger(JsonSubscribeProcessor.class); //private static DateTimeFormatter fmt = ISODateTimeFormat.dateTime(); public void process(Exchange exchange) throws Exception { try { if(exchange.getIn().getBody()==null ||!(exchange.getIn().getBody() instanceof Json)) return; String wsSession = exchange.getIn().getHeader(WebsocketConstants.CONNECTION_KEY, String.class); if(wsSession==null){ if(logger.isDebugEnabled())logger.debug("WsSession is null:"+exchange.getIn().getHeaders()); //return; } Json json = exchange.getIn().getBody(Json.class); //avoid full signalk syntax if(json.has(vessels))return; if(json.has(CONTEXT) && (json.has(SUBSCRIBE) || json.has(UNSUBSCRIBE))){ json = handle(json, exchange.getIn().getHeaders()); exchange.getIn().setBody(json); }else{ if(logger.isDebugEnabled()){ logger.debug("Subscribe msg class: "+exchange.getIn().getBody().getClass()); logger.debug("Subscribe msg: "+exchange.getIn().getBody()); } } } catch (Exception e) { logger.error(e.getMessage(),e); } } /* * * <pre> { "context": "vessels.*", "subscribe": [ { "path": "navigation.position", "period": 120000, "format": "full", "policy": "fixed" }, { "path": "navigation.courseOverGround", "period": 120000, "format": "full", "policy": "fixed" } ] } {"context":"vessels.*","subscribe":[{"path":"navigation.position","period":120000,"format":"full","policy":"fixed"},{"path":"navigation.courseOverGround","period":120000,"format":"full","policy":"fixed"}]} </pre> */ //@Override public Json handle(Json node, Map<String, Object> headers) throws Exception { //deal with diff format if(logger.isDebugEnabled())logger.debug("Checking for subscribe "+node ); //go to context String context = node.at(CONTEXT).asString(); //TODO: is the context and path valid? A DOS attack is possible if we allow numerous crap/bad paths? Json subscriptions = node.at(SUBSCRIBE); if(subscriptions!=null){ //MQTT and STOMP wont have created proper session links if(node.has(WebsocketConstants.CONNECTION_KEY)){ String wsSession = node.at(WebsocketConstants.CONNECTION_KEY).asString(); if(node.has(ConfigConstants.OUTPUT_TYPE)){ String outputType = node.at(ConfigConstants.OUTPUT_TYPE).asString(); SubscriptionManagerFactory.getInstance().add(wsSession, wsSession, outputType,"127.0.0.1","127.0.0.1"); } } if(subscriptions.isArray()){ for(Json subscription: subscriptions.asJsonList()){ parseSubscribe(headers, context, subscription); } } if(logger.isDebugEnabled())logger.debug("processed subscribe "+node ); } Json unsubscriptions = node.at(UNSUBSCRIBE); if(unsubscriptions!=null){ if(unsubscriptions.isArray()){ for(Json subscription: unsubscriptions.asJsonList()){ parseUnsubscribe(headers, context, subscription); } } if(logger.isDebugEnabled())logger.debug("processed unsubscribe "+node ); } return node; } /** * * <pre>{ "path": "navigation.speedThroughWater", "period": 1000, "format": "delta", "policy": "ideal", "minPeriod": 200 } </pre> * @param context * @param subscription * @throws Exception */ private void parseSubscribe(Map<String, Object> headers, String context, Json subscription) throws Exception { //get values if(logger.isDebugEnabled())logger.debug("Parsing subscribe "+subscription ); String wsSession = (String) headers.get(WebsocketConstants.CONNECTION_KEY); String path = context+"."+subscription.at(PATH).asString(); long period = 1000; if(subscription.at(PERIOD)!=null)period = subscription.at(PERIOD).asInteger(); String format = FORMAT_DELTA; if(subscription.at(FORMAT)!=null)format=subscription.at(FORMAT).asString(); String policy = POLICY_FIXED; if(subscription.at(POLICY)!=null)policy=subscription.at(POLICY).asString(); long minPeriod = 0; if(subscription.at(MIN_PERIOD)!=null)minPeriod=subscription.at(MIN_PERIOD).asInteger(); Subscription sub = new Subscription(wsSession, path, period, minPeriod, format, policy); //STOMP, MQTT if(headers.containsKey(ConfigConstants.DESTINATION)){ sub.setDestination( headers.get(ConfigConstants.DESTINATION).toString()); } //sub.setActive(false); if(logger.isDebugEnabled())logger.debug("Created subscription; "+sub.toString() ); SubscriptionManagerFactory.getInstance().addSubscription(sub); } /** * * <pre>{ "path": "navigation.speedThroughWater", "period": 1000, "format": "delta", "policy": "ideal", "minPeriod": 200 } </pre> * @param context * @param subscription * @throws Exception */ private void parseUnsubscribe(Map<String, Object> headers, String context, Json subscription) throws Exception { //get values String path = context+"."+subscription.at(PATH).asString(); String wsSession = (String) headers.get(WebsocketConstants.CONNECTION_KEY); long period = 1000; if(subscription.at(PERIOD)!=null)period = subscription.at(PERIOD).asInteger(); String format = FORMAT_DELTA; if(subscription.at(FORMAT)!=null)format=subscription.at(FORMAT).asString(); String policy = POLICY_FIXED; if(subscription.at(POLICY)!=null)policy=subscription.at(POLICY).asString(); long minPeriod = 0; if(subscription.at(MIN_PERIOD)!=null)minPeriod=subscription.at(MIN_PERIOD).asInteger(); Subscription sub = new Subscription(wsSession, path, period, minPeriod, format, policy); SubscriptionManagerFactory.getInstance().removeSubscription(sub); } }