/* * * 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.server; import static nz.co.fortytwo.signalk.util.ConfigConstants.MAP_DIR; import static nz.co.fortytwo.signalk.util.ConfigConstants.OUTPUT_XMPP; import static nz.co.fortytwo.signalk.util.ConfigConstants.STATIC_DIR; import static nz.co.fortytwo.signalk.util.SignalKConstants.DEMO; //import static nz.co.fortytwo.signalk.util.ConfigConstants.UUID; import static nz.co.fortytwo.signalk.util.SignalKConstants.FORMAT_DELTA; import static nz.co.fortytwo.signalk.util.SignalKConstants.MSG_SRC_BUS; import static nz.co.fortytwo.signalk.util.SignalKConstants.MSG_SRC_IP; import static nz.co.fortytwo.signalk.util.SignalKConstants.MSG_TYPE; import static nz.co.fortytwo.signalk.util.SignalKConstants.POLICY_IDEAL; import static nz.co.fortytwo.signalk.util.SignalKConstants.SIGNALK_API; import static nz.co.fortytwo.signalk.util.SignalKConstants.SIGNALK_AUTH; import static nz.co.fortytwo.signalk.util.SignalKConstants.SIGNALK_CONFIG; import static nz.co.fortytwo.signalk.util.SignalKConstants.SIGNALK_DISCOVERY; import static nz.co.fortytwo.signalk.util.SignalKConstants.SIGNALK_INSTALL; import static nz.co.fortytwo.signalk.util.SignalKConstants.SIGNALK_LOGGER; import static nz.co.fortytwo.signalk.util.SignalKConstants.SIGNALK_RESTART; import static nz.co.fortytwo.signalk.util.SignalKConstants.SIGNALK_UPGRADE; import static nz.co.fortytwo.signalk.util.SignalKConstants.SIGNALK_UPLOAD; import static nz.co.fortytwo.signalk.util.SignalKConstants._SIGNALK_WS_TCP_LOCAL; import static nz.co.fortytwo.signalk.util.SignalKConstants.dot; import java.io.File; import java.net.Inet4Address; import java.util.Arrays; import java.util.UUID; import javax.jmdns.JmmDNS; import javax.jmdns.ServiceEvent; import javax.jmdns.ServiceListener; import org.apache.camel.Endpoint; import org.apache.camel.ExchangePattern; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.ahc.ws.WsEndpoint; import org.apache.camel.component.stomp.SkStompComponent; import org.apache.camel.component.websocket.SignalkWebsocketComponent; import org.apache.camel.impl.DefaultCamelContext; import org.apache.camel.impl.JndiRegistry; import org.apache.camel.impl.PropertyPlaceholderDelegateRegistry; import org.apache.camel.model.RouteDefinition; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.security.ConstraintMapping; import org.eclipse.jetty.security.ConstraintSecurityHandler; import org.eclipse.jetty.security.authentication.BasicAuthenticator; import org.eclipse.jetty.server.handler.ResourceHandler; import org.eclipse.jetty.util.security.Constraint; import org.jivesoftware.smack.SASLAuthentication; import org.jivesoftware.smackx.jiveproperties.JivePropertiesManager; import mjson.Json; import nz.co.fortytwo.signalk.model.SignalKModel; import nz.co.fortytwo.signalk.model.impl.SignalKModelFactory; import nz.co.fortytwo.signalk.processor.UploadProcessor; import nz.co.fortytwo.signalk.util.ConfigConstants; import nz.co.fortytwo.signalk.util.Util; /** * Main camel route definition to handle input to signalk * * * <ul> * <li>Basically all input is added to seda:input * <li>Message is converted to hashmap, processed,added to signalk model * <li>Output is sent out 1 sec. * </ul> * * * @author robert * */ public class RouteManager extends RouteBuilder { protected static final String JETTY_HTTP_0_0_0_0 = "jetty:http://0.0.0.0:"; private static Logger logger = LogManager.getLogger(RouteManager.class); //public static final String SEDA_INPUT = "seda:inputData?purgeWhenStopping=true&size=1000"; public static final String SEDA_INPUT = "activemq:queue:signalk.inputData?jmsMessageType=Text&timeToLive=10000&asyncConsumer=true&acceptMessagesWhileStopping=true"; public static final String SEDA_XMPP = "activemq:queue:signalk.xmppData?jmsMessageType=Text&timeToLive=10000&asyncConsumer=true&acceptMessagesWhileStopping=true"; public static final String SEDA_WEBSOCKETS = "seda:websockets?purgeWhenStopping=true&size=1000"; public static final String DIRECT_STOMP = "direct:stomp"; public static final String DIRECT_MQTT = "direct:mqtt"; public static final String DIRECT_XMPP = "direct:xmpp"; public static final String DIRECT_TCP = "seda:tcp?purgeWhenStopping=true&size=1000"; public static final String SEDA_NMEA = "seda:nmeaOutput?purgeWhenStopping=true&size=100"; public static final String SEDA_COMMON_OUT = "seda:commonOut?purgeWhenStopping=true&size=100"; public static final String STOMP = "skStomp:queue:signalk?brokerURL=tcp://0.0.0.0:"+Util.getConfigPropertyInt(ConfigConstants.STOMP_PORT); public static final String MQTT = "mqtt:signalk?host=tcp://0.0.0.0:"+Util.getConfigPropertyInt(ConfigConstants.MQTT_PORT); private int wsPort = 3000; private int restPort = 8080; //private String streamUrl; private SerialPortManager serialPortManager; private SignalKModel signalkModel=SignalKModelFactory.getInstance(); private NettyServer skServer; private NettyServer nmeaServer; protected RouteManager() { // web socket on port 3000 logger.info(" Websocket port:"+Util.getConfigPropertyInt(ConfigConstants.WEBSOCKET_PORT)); wsPort=Util.getConfigPropertyInt(ConfigConstants.WEBSOCKET_PORT); logger.info(" Signalk REST API port:"+Util.getConfigPropertyInt(ConfigConstants.REST_PORT)); restPort=Util.getConfigPropertyInt(ConfigConstants.REST_PORT); } @Override public void configure() throws Exception { configure0(); } public void configure0() throws Exception { //XMPP JivePropertiesManager.setJavaObjectEnabled(true); SASLAuthentication.unsupportSASLMechanism("DIGEST-MD5"); SASLAuthentication.unregisterSASLMechanism("DIGEST-MD5"); SASLAuthentication.supportSASLMechanism("PLAIN",0); //SmackConfiguration.DEBUG_ENABLED=true; errorHandler(deadLetterChannel("direct:fail") .useOriginalMessage() .maximumRedeliveries(1) .redeliveryDelay(1000)); from ("direct:fail").id("Fail") .to("log:nz.co.fortytwo.signalk.error?level=ERROR&showAll=true"); SignalKModelFactory.load(signalkModel); //set shutdown quickly, 5 min is too long CamelContextFactory.getInstance().getShutdownStrategy().setShutdownNowOnTimeout(true); CamelContextFactory.getInstance().getShutdownStrategy().setTimeout(10); //CamelContextFactory.getInstance().addComponent("activemq", ActiveMQComponent.activeMQComponent("vm://localhost?broker.persistent=false")); //Netty tcp server skServer = new NettyServer(null, ConfigConstants.OUTPUT_TCP); skServer.setTcpPort(Util.getConfigPropertyInt(ConfigConstants.TCP_PORT)); skServer.setUdpPort(Util.getConfigPropertyInt(ConfigConstants.UDP_PORT)); skServer.run(); nmeaServer = new NettyServer(null, ConfigConstants.OUTPUT_NMEA); nmeaServer.setTcpPort(Util.getConfigPropertyInt(ConfigConstants.TCP_NMEA_PORT)); nmeaServer.setUdpPort(Util.getConfigPropertyInt(ConfigConstants.UDP_NMEA_PORT)); nmeaServer.run(); // start a serial port manager if(serialPortManager==null){ serialPortManager = new SerialPortManager(); } new Thread(serialPortManager).start(); // main input to destination route // put all input into signalk model SignalkRouteFactory.configureInputRoute(this, SEDA_INPUT); File htmlRoot = new File(Util.getConfigProperty(ConfigConstants.STATIC_DIR)); log.info("Serving static files from "+htmlRoot.getAbsolutePath()); //restlet //bind in registry PropertyPlaceholderDelegateRegistry registry = (PropertyPlaceholderDelegateRegistry) CamelContextFactory.getInstance().getRegistry(); JndiRegistry reg = (JndiRegistry)registry.getRegistry(); if(reg.lookup("staticHandler")==null){ ResourceHandler staticHandler = new ResourceHandler(); staticHandler.setResourceBase(Util.getConfigProperty(ConfigConstants.STATIC_DIR)); staticHandler.setDirectoriesListed(false); MimeTypes mimeTypes = staticHandler.getMimeTypes(); mimeTypes.addMimeMapping("log", MimeTypes.TEXT_HTML_UTF_8); staticHandler.setMimeTypes(mimeTypes); //static files reg.bind("staticHandler",staticHandler ); } if(reg.lookup("staticConfigHandler")==null){ Constraint constraint = new Constraint("BASIC", "rolename"); constraint.setAuthenticate(true); ConstraintMapping mapping = new ConstraintMapping(); mapping.setPathSpec("/config/*"); mapping.setConstraint(constraint); ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler(); securityHandler.setAuthenticator(new BasicAuthenticator()); securityHandler.setLoginService(new SignalkLoginService()); securityHandler.addConstraintMapping(mapping); ResourceHandler staticHandler = new ResourceHandler(); staticHandler.setResourceBase(Util.getConfigProperty(ConfigConstants.STATIC_DIR)+"config/"); staticHandler.setDirectoriesListed(false); MimeTypes mimeTypes = staticHandler.getMimeTypes(); mimeTypes.addMimeMapping("log", MimeTypes.TEXT_HTML_UTF_8); staticHandler.setMimeTypes(mimeTypes); securityHandler.setHandler(staticHandler); //static files reg.bind("staticConfigHandler",securityHandler ); } if(reg.lookup("securityHandler")==null){ Constraint constraint = new Constraint("BASIC", "rolename"); constraint.setAuthenticate(true); ConstraintMapping configMapping = new ConstraintMapping(); configMapping.setPathSpec("/signalk/v1/config/*"); configMapping.setConstraint(constraint); ConstraintMapping installMapping = new ConstraintMapping(); installMapping.setPathSpec("/signalk/v1/install/*"); installMapping.setConstraint(constraint); ConstraintMapping upgradeMapping = new ConstraintMapping(); upgradeMapping.setPathSpec("/signalk/v1/upgrade/*"); upgradeMapping.setConstraint(constraint); ConstraintMapping restartMapping = new ConstraintMapping(); restartMapping.setPathSpec("/signalk/v1/restart"); restartMapping.setConstraint(constraint); ConstraintMapping uploadMapping = new ConstraintMapping(); uploadMapping.setPathSpec("/signalk/v1/upload/*"); uploadMapping.setConstraint(constraint); ConstraintMapping loggerMapping = new ConstraintMapping(); loggerMapping.setPathSpec("/signalk/v1/logger/*"); loggerMapping.setConstraint(constraint); ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler(); securityHandler.setAuthenticator(new BasicAuthenticator()); securityHandler.setLoginService(new SignalkLoginService()); securityHandler.addConstraintMapping(installMapping); securityHandler.addConstraintMapping(upgradeMapping); securityHandler.addConstraintMapping(restartMapping); securityHandler.addConstraintMapping(loggerMapping); securityHandler.addConstraintMapping(configMapping); securityHandler.addConstraintMapping(uploadMapping); reg.bind("securityHandler",securityHandler ); } restConfiguration().component("jetty") .consumerProperty("matchOnUriPrefix", "true") .componentProperty("matchOnUriPrefix", "true") .host("0.0.0.0").port(restPort); //websockets if(CamelContextFactory.getInstance().getComponent("skWebsocket")==null){ SignalkWebsocketComponent skws = new SignalkWebsocketComponent(); CamelContextFactory.getInstance().addComponent("skWebsocket", skws); } //STOMP if(CamelContextFactory.getInstance().getComponent("skStomp")==null){ CamelContextFactory.getInstance().addComponent("skStomp", new SkStompComponent()); } //setup routes SignalkRouteFactory.configureWebsocketTxRoute(this, SEDA_WEBSOCKETS, wsPort); SignalkRouteFactory.configureWebsocketRxRoute(this, SEDA_INPUT, wsPort); SignalkRouteFactory.configureTcpServerRoute(this, DIRECT_TCP, skServer, ConfigConstants.OUTPUT_TCP); SignalkRouteFactory.configureTcpServerRoute(this, SEDA_NMEA, nmeaServer, ConfigConstants.OUTPUT_NMEA); SignalkRouteFactory.configureCommonOut(this); SignalkRouteFactory.configureHeartbeatRoute(this,"timer://heartbeat?fixedRate=true&period=1000"); SignalkRouteFactory.configureAuthRoute(this, JETTY_HTTP_0_0_0_0 + restPort + SIGNALK_AUTH+"?sessionSupport=true&matchOnUriPrefix=true&handlers=#securityHandler,#staticHandler,#staticConfigHandler&enableJMX=true&enableCORS=true"); SignalkRouteFactory.configureRestRoute(this, JETTY_HTTP_0_0_0_0 + restPort + SIGNALK_DISCOVERY+"?sessionSupport=true&matchOnUriPrefix=false&enableJMX=true&enableCORS=true","REST Discovery"); SignalkRouteFactory.configureRestRoute(this, JETTY_HTTP_0_0_0_0 + restPort + SIGNALK_API+"?sessionSupport=true&matchOnUriPrefix=true&enableJMX=true&enableCORS=true","REST Api"); SignalkRouteFactory.configureRestConfigRoute(this, JETTY_HTTP_0_0_0_0 + restPort + SIGNALK_CONFIG+"?sessionSupport=true&matchOnUriPrefix=true&enableJMX=true&enableCORS=false","Config Api"); SignalkRouteFactory.configureRestLoggerRoute(this, JETTY_HTTP_0_0_0_0 + restPort + SIGNALK_LOGGER+"?sessionSupport=true&matchOnUriPrefix=true&enableJMX=true&enableCORS=false","Logger"); SignalkRouteFactory.configureRestUploadRoute(this, SIGNALK_UPLOAD,"Upload"); if(Util.getConfigPropertyBoolean(ConfigConstants.ALLOW_INSTALL)){ SignalkRouteFactory.configureInstallRoute(this, JETTY_HTTP_0_0_0_0 + restPort + SIGNALK_INSTALL+"?sessionSupport=true&matchOnUriPrefix=true&enableJMX=true", "REST Install"); } if(Util.getConfigPropertyBoolean(ConfigConstants.ALLOW_UPGRADE)){ SignalkRouteFactory.configureInstallRoute(this, JETTY_HTTP_0_0_0_0 + restPort + SIGNALK_UPGRADE+"?sessionSupport=true&matchOnUriPrefix=true&enableJMX=true", "REST Upgrade"); } // timed actions SignalkRouteFactory.configureBackgroundTimer(this, "timer://background?fixedRate=true&period=60000"); SignalkRouteFactory.configureWindTimer(this, "timer://wind?fixedRate=true&period=1000"); SignalkRouteFactory.configureDepthTimer(this, "timer://depth?fixedRate=true&period=1000"); SignalkRouteFactory.configureAnchorWatchTimer(this, "timer://anchorWatch?fixedRate=true&period=5000"); SignalkRouteFactory.configureAlarmsTimer(this, "timer://alarms?fixedRate=true&period=1000"); if(Util.getConfigPropertyBoolean(ConfigConstants.GENERATE_NMEA0183)){ SignalkRouteFactory.configureNMEA0183Timer(this, "timer://nmea0183?fixedRate=true&period=1000"); } //STOMP if(Util.getConfigPropertyBoolean(ConfigConstants.START_STOMP)){ from("skStomp:queue:signalk.put").id("STOMP In") .setHeader(ConfigConstants.OUTPUT_TYPE, constant(ConfigConstants.OUTPUT_STOMP)) .setHeader(MSG_SRC_BUS, constant("stomp.queue:signalk.put")) .to(SEDA_INPUT).id(SignalkRouteFactory.getName("SEDA_INPUT")); } //MQTT if(Util.getConfigPropertyBoolean(ConfigConstants.START_MQTT)){ from(MQTT+"&subscribeTopicName=signalk.put").id("MQTT In") .transform(body().convertToString()) .setHeader(ConfigConstants.OUTPUT_TYPE, constant(ConfigConstants.OUTPUT_MQTT)) .setHeader(MSG_SRC_BUS, constant("mqtt.queue:signalk.put")) .to(SEDA_INPUT).id(SignalkRouteFactory.getName("SEDA_INPUT")); } //start any clients if they exist //WS Json wsClients = Util.getConfigJsonArray(ConfigConstants.CLIENT_WS); logger.info(" WS client connections : "+wsClients); if(wsClients!=null){ for(Object client: wsClients.asList()){ logger.info(" Starting WS client connection to url:ahc-ws://"+client); WsEndpoint wsEndpoint = (WsEndpoint)getContext().getEndpoint("ahc-ws://"+client); setupClient("ahc-ws://"+client,client.toString(),"ws"); wsEndpoint.connect(); } } //TCP Json tcpClients = Util.getConfigJsonArray(ConfigConstants.CLIENT_TCP); if(tcpClients!=null){ for(Object client: tcpClients.asList()){ setupClient("netty4:tcp://"+client+"?clientMode=true&textline=true",client.toString(),"tcp"); } } //MQTT Json mqttClients = Util.getConfigJsonArray(ConfigConstants.CLIENT_MQTT); if(mqttClients!=null){ for(Object client: mqttClients.asList()){ setupClient("mqtt://"+client,client.toString(),"mqtt"); } } //STOMP //TODO: test stomp client actually works! Json stompClients = Util.getConfigJsonArray(ConfigConstants.CLIENT_STOMP); if(stompClients!=null){ for(Object client: stompClients.asList()){ setupClient("stomp://"+client,client.toString(),"stomp"); } } //XMPP //"xmpp": [{"server":"xmpp.www.42.co.nz","passwd":"motu","user":"motu","room":"signalk"}] Json xmppClients = Util.getConfigJsonArray(ConfigConstants.XMPP); if(xmppClients!=null){ for(Json client: xmppClients.asJsonList()){ String server = client.at("server").asString(); String user = client.at("user").asString(); String passwd = client.at("passwd").asString(); String room = client.at("room").asString(); String filter = client.at("filter").asString(); Endpoint xmppEndpoint = getContext().getEndpoint("xmpp://"+server+"?testConnectionOnStartup=false&room="+room+"&user="+user+"&password="+passwd+"&resource="+user+"&serviceName="+server); //receive setupClient(xmppEndpoint,server+dot+room,"xmpp"); //tx from(SEDA_XMPP+"&selector="+ConfigConstants.DESTINATION+" %3D '"+room+"'").id("XMPP out: "+room) .convertBodyTo(String.class) .to(xmppEndpoint).id("XMPP Service:"+room); //and subscribe String wsSession = UUID.randomUUID().toString(); SubscriptionManagerFactory.getInstance().add(wsSession, wsSession,OUTPUT_XMPP,Inet4Address.getLocalHost().getHostAddress(), Inet4Address.getByName(server).getHostAddress()); for(String f:filter.split(",")){ Subscription sub = new Subscription(wsSession, f, 5000, 1000, FORMAT_DELTA, POLICY_IDEAL); sub.setDestination(room); SubscriptionManagerFactory.getInstance().addSubscription(sub); } } } //reload charts into resources reloadCharts(); //restart support from(JETTY_HTTP_0_0_0_0 + restPort + SIGNALK_RESTART).id("Restart route") .setExchangePattern(ExchangePattern.InOut) .setBody(constant("Restarting now..")) .to("file://./conf/?fileName=signalk-restart"); //Demo mode if (Util.getConfigPropertyBoolean(ConfigConstants.DEMO)) { String streamUrl = Util.getConfigProperty(ConfigConstants.STREAM_URL); logger.info(" Demo streaming url:"+Util.getConfigProperty(ConfigConstants.STREAM_URL)); from("file://./src/test/resources/samples/?move=done&fileName=" + streamUrl).id("demo feed") .onException(Exception.class).handled(true).maximumRedeliveries(0) .to("log:nz.co.fortytwo.signalk.model.receive?level=ERROR&showException=true&showStackTrace=true") .end() .split(body().regexTokenize("[\r\n|\n|\r]")).streaming() .convertBodyTo(String.class) .throttle(4).timePeriodMillis(1000) .setHeader(MSG_TYPE, constant(DEMO)) //.setHeader(MSG_SRC_IP,constant("127.0.0.1")) .setHeader(MSG_SRC_BUS, constant("demo")) .to(SEDA_INPUT).id(SignalkRouteFactory.getName("SEDA_INPUT")) .end(); //and copy it back again to rerun it from("file://./src/test/resources/samples/done?fileName=" + streamUrl).id("demo restart") .onException(Exception.class).handled(true).maximumRedeliveries(0) .end() .to("file://./src/test/resources/samples/?fileName=" + streamUrl); } SignalkRouteFactory.startLogRoutes(this, JETTY_HTTP_0_0_0_0, restPort); if (Util.getConfigPropertyBoolean(ConfigConstants.ZEROCONF_AUTO)) { startMdnsAutoconnect(); } } private void reloadCharts() { String staticDir = Util.getConfigProperty(STATIC_DIR); if(!staticDir.endsWith("/")){ staticDir=staticDir+"/"; } File mapDir = new File(staticDir+Util.getConfigProperty(MAP_DIR)); logger.debug("Reloading charts from: "+mapDir.getAbsolutePath()); if(mapDir==null || !mapDir.exists() || mapDir.listFiles()==null)return; UploadProcessor processor = new UploadProcessor(); for(File chart:mapDir.listFiles()){ if(chart.isDirectory()){ logger.debug("Reloading: "+chart.getName()); try { processor.loadChart(chart.getName()); } catch (Exception e) { logger.warn(e.getMessage()); } } } } private void setupClient(String endpoint, String client, String serviceName) { setupClient(getContext().getEndpoint(endpoint), client, serviceName); } private void setupClient(Endpoint endpoint, String client, String serviceName) { from(endpoint).id(serviceName.toUpperCase()+" Client:"+client) .onException(Exception.class).handled(true).maximumRedeliveries(0) .to("log:nz.co.fortytwo.signalk.client."+serviceName+"?level=ERROR&showException=true&showStackTrace=true") .end() .to("log:nz.co.fortytwo.signalk.client."+serviceName+"?level=DEBUG") .convertBodyTo(String.class) .setHeader(MSG_SRC_BUS, constant(serviceName+"."+client.toString().replace('.', '_'))) .to(SEDA_INPUT); } private void startMdnsAutoconnect() { //now listen and report other services logger.info("Starting jmdns listener.."); JmmDNS.Factory.getInstance().addServiceListener(_SIGNALK_WS_TCP_LOCAL, new ServiceListener() { @Override public void serviceResolved(ServiceEvent evt) { try { String name = evt.getName(); String thisHost = evt.getDNS().getInetAddress().getHostAddress(); logger.info("Resolved mDns service:"+name+" at "+thisHost); logger.debug(name+" Server:"+evt.getInfo().getServer()); String[] remoteHost = evt.getInfo().getHostAddresses(); logger.debug(name+" Remotehost:"+remoteHost[0]); logger.debug(name+" URLs:"+Arrays.toString(evt.getInfo().getURLs())); if(thisHost.startsWith(remoteHost[0]) || evt.getDNS().getInetAddress().isLinkLocalAddress() || evt.getDNS().getInetAddress().isLoopbackAddress()){ logger.info(name+" Found own host: "+remoteHost[0]+", ignoring.."); return; } if(remoteHost[0].startsWith("[fe80") || evt.getDNS().getInetAddress().isLinkLocalAddress()){ logger.info(name+" Found ipv6 host: "+remoteHost[0]+", ignoring.."); return; } //we want to connect here String url =evt.getInfo().getURLs()[0]; if(StringUtils.isNotBlank(url)){ logger.info(name+" Connecting to: "+url); url=url.substring(url.indexOf("://")+3); url=url+"/v1/stream"; logger.info(" Starting WS connection to url:ahc-ws://"+url); startWsClient(url); } } catch (Exception e) { logger.error(e); } } @Override public void serviceRemoved(ServiceEvent evt) { logger.info("Lost mDns service:"+evt.getName()); } @Override public void serviceAdded(ServiceEvent evt) { logger.info("Found mDns service:"+evt.getName()+" at "+evt.getType()); } }); logger.info("Started jmdns listener"); } private void startWsClient(String client) throws Exception { WsEndpoint wsEndpoint = (WsEndpoint)getContext().getEndpoint("ahc-ws://"+client); RouteDefinition route = from(wsEndpoint); route.onException(Exception.class).handled(true).maximumRedeliveries(0) .to("log:nz.co.fortytwo.signalk.client.ws?level=ERROR&showException=true&showStackTrace=true") .end() .to("log:nz.co.fortytwo.signalk.client.ws?level=DEBUG") .convertBodyTo(String.class) .setHeader(MSG_SRC_BUS, constant("ws."+client.toString().replace('.', '_'))) .to(SEDA_INPUT); route.setId("Websocket Client:"+client); ((DefaultCamelContext)CamelContextFactory.getInstance()).addRouteDefinition(route); ((DefaultCamelContext)CamelContextFactory.getInstance()).startRoute(route.getId()); wsEndpoint.connect(); } public void stopNettyServers(){ if(skServer!=null){ skServer.shutdownServer(); skServer=null; } if(nmeaServer!=null){ nmeaServer.shutdownServer(); nmeaServer=null; } } /** * When the serial port is used to read from the arduino this must be called to shut * down the readers, which are in their own threads. */ public void stopSerial() { serialPortManager.stopSerial(); serialPortManager=null; } }