001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.runtime.gateway; 020 021import java.io.BufferedReader; 022import java.io.IOException; 023import java.io.InputStreamReader; 024import java.io.PrintWriter; 025import java.io.Reader; 026import java.io.Writer; 027import java.net.ServerSocket; 028import java.net.Socket; 029import java.util.ArrayList; 030import java.util.Iterator; 031import java.util.List; 032import java.util.Map; 033 034import lucee.commons.lang.ExceptionUtil; 035import lucee.loader.engine.CFMLEngine; 036import lucee.loader.engine.CFMLEngineFactory; 037import lucee.runtime.exp.PageException; 038import lucee.runtime.type.Struct; 039import lucee.runtime.util.Cast; 040import lucee.runtime.util.Creation; 041 042public class SocketGateway implements GatewayPro { 043 044 private GatewayEnginePro engine; 045 private int port; 046 private String welcomeMessage="Welcome to the Lucee Socket Gateway"; 047 048 private String id; 049 private CFMLEngine cfmlEngine; 050 private Cast caster; 051 private Creation creator; 052 private List<SocketServerThread> sockets=new ArrayList<SocketServerThread>(); 053 private ServerSocket serverSocket; 054 protected int state=STOPPED; 055 private String cfcPath; 056 057 058 @Override 059 public void init(GatewayEnginePro engine, String id, String cfcPath, Map config)throws GatewayException { 060 this.engine=engine; 061 cfmlEngine=CFMLEngineFactory.getInstance(); 062 caster=cfmlEngine.getCastUtil(); 063 creator = cfmlEngine.getCreationUtil(); 064 this.cfcPath=cfcPath; 065 this.id=id; 066 067 // config 068 Object oPort=config.get("port"); 069 port=caster.toIntValue(oPort, 1225); 070 071 Object oWM=config.get("welcomeMessage"); 072 String strWM=caster.toString(oWM,"").trim(); 073 if(strWM.length()>0)welcomeMessage=strWM; 074 } 075 076 077 public void doStart() { 078 state = STARTING; 079 try { 080 createServerSocket(); 081 state = RUNNING; 082 do { 083 try { 084 SocketServerThread sst = new SocketServerThread(serverSocket.accept()); 085 sst.start(); 086 sockets.add(sst); 087 } 088 catch (Throwable t) { 089 ExceptionUtil.rethrowIfNecessary(t); 090 error("Failed to listen on Socket ["+id+"] on port ["+port+"]: " + t.getMessage()); 091 } 092 } 093 while (getState()==RUNNING || getState()==STARTING); 094 095 close(serverSocket); 096 serverSocket = null; 097 } 098 catch (Throwable e) { 099 ExceptionUtil.rethrowIfNecessary(e); 100 state=FAILED; 101 error("Error in Socet Gateway ["+id+"]: " + e.getMessage()); 102 e.printStackTrace(); 103 //throw CFMLEngineFactory.getInstance().getCastUtil().toPageException(e); 104 } 105 } 106 107 public void doStop() { 108 state = STOPPING; 109 try{ 110 111 // close all open connections 112 Iterator<SocketServerThread> it = sockets.iterator(); 113 while (it.hasNext()) { 114 close(it.next().socket); 115 } 116 117 // close server socket 118 close(serverSocket); 119 serverSocket = null; 120 state = STOPPED; 121 } 122 catch(Throwable e){ 123 ExceptionUtil.rethrowIfNecessary(e); 124 state=FAILED; 125 error("Error in Socket Gateway ["+id+"]: " + e.getMessage()); 126 e.printStackTrace(); 127 //throw CFMLEngineFactory.getInstance().getCastUtil().toPageException(e); 128 } 129 } 130 131 private void createServerSocket() throws PageException, RuntimeException { 132 try { 133 serverSocket = new ServerSocket(port); 134 } 135 catch (Throwable t) { 136 ExceptionUtil.rethrowIfNecessary(t); 137 error("Failed to start Socket Gateway ["+id+"] on port ["+port+"] " +t.getMessage()); 138 throw CFMLEngineFactory.getInstance().getCastUtil().toPageException(t); 139 } 140 } 141 142 143 144 145 private void invokeListener(String line, String originatorID) { 146 147 Struct data=creator.createStruct(); 148 data.setEL(creator.createKey("message"), line); 149 Struct event=creator.createStruct(); 150 event.setEL(creator.createKey("data"), data); 151 event.setEL(creator.createKey("originatorID"), originatorID); 152 153 event.setEL(creator.createKey("cfcMethod"), "onIncomingMessage"); 154 event.setEL(creator.createKey("cfcTimeout"), new Double(10)); 155 event.setEL(creator.createKey("cfcPath"), cfcPath); 156 157 event.setEL(creator.createKey("gatewayType"), "Socket"); 158 event.setEL(creator.createKey("gatewayId"), id); 159 160 161 162 if (engine.invokeListener(this, "onIncomingMessage", event)) 163 info("Socket Gateway Listener ["+id+"] invoked."); 164 else 165 error("Failed to call Socket Gateway Listener ["+id+"]"); 166 } 167 168 169 private class SocketServerThread extends Thread { 170 private Socket socket; 171 private PrintWriter out; 172 private String _id; 173 174 public SocketServerThread(Socket socket) throws IOException { 175 this.socket = socket; 176 out = new PrintWriter(socket.getOutputStream(), true); 177 this._id=String.valueOf(hashCode()); 178 } 179 180 public void run() { 181 BufferedReader in = null; 182 try { 183 in = new BufferedReader(new InputStreamReader(socket.getInputStream())); 184 out.println(welcomeMessage); 185 out.print("> "); 186 String line; 187 while ((line = in.readLine()) != null) { 188 if (line.trim().equals("exit")) break; 189 invokeListener(line,_id); 190 } 191 //socketRegistry.remove(this.getName()); 192 } 193 catch (Throwable t) { 194 ExceptionUtil.rethrowIfNecessary(t); 195 error("Failed to read from Socket Gateway ["+id+"]: " + t.getMessage()); 196 } 197 finally{ 198 close(out); 199 out=null; 200 close(in); 201 close(socket); 202 sockets.remove(this); 203 } 204 } 205 206 public void writeOutput(String str) { 207 out.println(str); 208 out.print("> "); 209 } 210 } 211 212 213 214 215 216 217 218 219 public String sendMessage(Map _data) { 220 Struct data=caster.toStruct(_data, null, false); 221 String msg = (String) data.get("message",null); 222 String originatorID=(String) data.get("originatorID",null); 223 224 String status="OK"; 225 if (msg!=null ) { 226 227 Iterator<SocketServerThread> it = sockets.iterator(); 228 SocketServerThread sst; 229 try { 230 boolean hasSend=false; 231 while(it.hasNext()){ 232 sst=it.next(); 233 if(originatorID!=null && !sst._id.equalsIgnoreCase(originatorID)) continue; 234 sst.writeOutput(msg); 235 hasSend=true; 236 } 237 238 if(!hasSend) { 239 if(sockets.size()==0) { 240 error("There is no connection"); 241 status = "EXCEPTION"; 242 } 243 else { 244 it = sockets.iterator(); 245 StringBuilder sb=new StringBuilder(); 246 while(it.hasNext()){ 247 if(sb.length()>0) sb.append(", "); 248 sb.append(it.next()._id); 249 } 250 error("There is no connection with originatorID ["+originatorID+"], available originatorIDs are ["+sb+"]"); 251 status = "EXCEPTION"; 252 } 253 } 254 } 255 catch (Exception e) { 256 e.printStackTrace(); 257 error("Failed to send message with exception: " + e.toString()); 258 status = "EXCEPTION"; 259 } 260 } 261 return status; 262 } 263 264 265 @Override 266 public void doRestart() { 267 doStop(); 268 doStart(); 269 } 270 271 272 273 @Override 274 public String getId() { 275 return id; 276 } 277 278 @Override 279 public int getState() { 280 return state; 281 } 282 283 284 285 @Override 286 public Object getHelper() { 287 return null; 288 } 289 290 291 public void info(String msg) { 292 engine.log(this,GatewayEnginePro.LOGLEVEL_INFO,msg); 293 } 294 295 public void error(String msg) { 296 engine.log(this,GatewayEnginePro.LOGLEVEL_ERROR,msg); 297 } 298 299 300 private void close(Writer writer) { 301 if(writer==null) return; 302 try{ 303 writer.close(); 304 } 305 catch(Throwable t){ 306 ExceptionUtil.rethrowIfNecessary(t); 307 } 308 } 309 private void close(Reader reader) { 310 if(reader==null) return; 311 try{ 312 reader.close(); 313 } 314 catch(Throwable t){ 315 ExceptionUtil.rethrowIfNecessary(t); 316 } 317 } 318 private void close(Socket socket) { 319 if(socket==null) return; 320 try{ 321 socket.close(); 322 } 323 catch(Throwable t){ 324 ExceptionUtil.rethrowIfNecessary(t); 325 } 326 } 327 private void close(ServerSocket socket) { 328 if(socket==null) return; 329 try{ 330 socket.close(); 331 } 332 catch(Throwable t){ 333 ExceptionUtil.rethrowIfNecessary(t); 334 } 335 } 336 337}