001package runwar; 002 003import java.io.File; 004import java.io.FileNotFoundException; 005import java.io.FileOutputStream; 006import java.io.IOException; 007import java.io.PrintStream; 008import java.io.BufferedReader; 009import java.io.InputStreamReader; 010import java.io.PrintWriter; 011import java.lang.management.ManagementFactory; 012import java.lang.reflect.Method; 013import java.net.InetAddress; 014import java.net.ServerSocket; 015import java.net.Socket; 016import java.net.URL; 017import java.net.URLClassLoader; 018import java.net.UnknownHostException; 019import java.nio.file.Files; 020import java.nio.file.Paths; 021import java.util.ArrayList; 022import java.util.Arrays; 023import java.util.List; 024import java.util.Timer; 025import java.util.TimerTask; 026import java.awt.Image; 027 028import javax.net.SocketFactory; 029import javax.servlet.DispatcherType; 030 031import runwar.logging.Logger; 032import runwar.logging.LogSubverter; 033import runwar.mariadb4j.MariaDB4jManager; 034import runwar.options.CommandLineHandler; 035import runwar.options.ServerOptions; 036import runwar.undertow.MappedResourceManager; 037import runwar.undertow.WebXMLParser; 038import runwar.util.TeeOutputStream; 039import io.undertow.Handlers; 040import io.undertow.Undertow; 041import io.undertow.Undertow.Builder; 042import io.undertow.predicate.Predicates; 043import io.undertow.server.HandlerWrapper; 044import io.undertow.server.HttpHandler; 045import io.undertow.server.HttpServerExchange; 046import io.undertow.server.handlers.PathHandler; 047import io.undertow.server.handlers.PredicateHandler; 048import io.undertow.server.handlers.cache.CacheHandler; 049import io.undertow.server.handlers.cache.DirectBufferCache; 050import io.undertow.server.handlers.encoding.ContentEncodingRepository; 051import io.undertow.server.handlers.encoding.EncodingHandler; 052import io.undertow.server.handlers.encoding.GzipEncodingProvider; 053import io.undertow.server.handlers.resource.ResourceHandler; 054import io.undertow.servlet.api.DeploymentInfo; 055import io.undertow.servlet.api.DeploymentManager; 056import io.undertow.servlet.api.FilterInfo; 057import io.undertow.servlet.api.MimeMapping; 058import io.undertow.servlet.api.ServletInfo; 059import io.undertow.servlet.handlers.DefaultServlet; 060import io.undertow.util.Headers; 061import io.undertow.util.MimeMappings; 062import static io.undertow.servlet.Servlets.defaultContainer; 063import static io.undertow.servlet.Servlets.deployment; 064import static io.undertow.servlet.Servlets.servlet; 065import static java.nio.file.StandardCopyOption.*; 066 067public class Server { 068 069 private static Logger log = Logger.getLogger("RunwarLogger"); 070 private TeeOutputStream tee; 071 private static ServerOptions serverOptions; 072 private static MariaDB4jManager mariadb4jManager; 073 static volatile boolean listening; 074 int portNumber; 075 int socketNumber; 076 private DeploymentManager manager; 077 private Undertow undertow; 078 079 private String PID; 080 private String serverState = ServerState.STOPPED; 081 082 private static URLClassLoader _classLoader; 083 084 private String serverName = "default"; 085 private File statusFile = null; 086 public static final String bar = "******************************************************************************"; 087 088 public Server() { 089 } 090 091 // for openBrowser 092 public Server(int seconds) { 093 Timer timer = new Timer(); 094 timer.schedule(this.new OpenBrowserTask(), seconds * 1000); 095 } 096 097 protected void initClassLoader(List<URL> _classpath) { 098 if (_classLoader == null) { 099 log.debug("Loading classes from lib dir"); 100 if( _classpath != null && _classpath.size() > 0) { 101 log.debugf("classpath: %s",_classpath); 102 // _classLoader = new URLClassLoader(_classpath.toArray(new URL[_classpath.size()]),Thread.currentThread().getContextClassLoader()); 103 // _classLoader = new URLClassLoader(_classpath.toArray(new URL[_classpath.size()]),ClassLoader.getSystemClassLoader()); 104 // _classLoader = new URLClassLoader(_classpath.toArray(new URL[_classpath.size()])); 105 _classLoader = new URLClassLoader(_classpath.toArray(new URL[_classpath.size()])); 106 // _classLoader = new XercesFriendlyURLClassLoader(_classpath.toArray(new URL[_classpath.size()]),ClassLoader.getSystemClassLoader()); 107 // Thread.currentThread().setContextClassLoader(_classLoader); 108 } else { 109 _classLoader = new URLClassLoader(null); 110 } 111 } 112 } 113 114 protected void setClassLoader(URLClassLoader classLoader){ 115 _classLoader = classLoader; 116 } 117 118 public static URLClassLoader getClassLoader(){ 119 return _classLoader; 120 } 121 122 public void startServer(String[] args, URLClassLoader classLoader) throws Exception { 123 setClassLoader(classLoader); 124 startServer(args); 125 } 126 127 public void ensureJavaVersion() { 128 Class<?> nio; 129 log.debug("Checking that we're running on > java7"); 130 try{ 131 nio = Server.class.getClassLoader().loadClass("java.nio.charset.StandardCharsets"); 132 nio.getClass().getName(); 133 } catch (java.lang.ClassNotFoundException e) { 134 throw new RuntimeException("Could not load NIO! Are we running on Java 7 or greater? Sorry, exiting..."); 135 } 136 } 137 138 @SuppressWarnings({ "rawtypes", "unchecked" }) 139 public void startServer(final String[] args) throws Exception { 140 ensureJavaVersion(); 141 serverState = ServerState.STARTING; 142 serverOptions = CommandLineHandler.parseArguments(args); 143 if(serverOptions.getAction().equals("stop")){ 144 Stop.stopServer(args,true); 145 } 146 serverName = serverOptions.getServerName(); 147 portNumber = serverOptions.getPortNumber(); 148 socketNumber = serverOptions.getSocketNumber(); 149 String cfengine = serverOptions.getCFEngineName(); 150 String processName = serverOptions.getProcessName(); 151 String contextPath = serverOptions.getContextPath(); 152 String host = serverOptions.getHost(); 153 File warFile = serverOptions.getWarFile(); 154 if (serverOptions.getStatusFile() != null) { 155 statusFile = serverOptions.getStatusFile(); 156 } 157 String warPath = serverOptions.getWarPath(); 158 String loglevel = serverOptions.getLoglevel(); 159 char[] stoppassword = serverOptions.getStopPassword(); 160 Long transferMinSize= serverOptions.getTransferMinSize(); 161 162 if (serverOptions.isBackground()) { 163 setServerState(ServerState.STARTING_BACKGROUND); 164 // this will eventually system.exit(); 165 List<String> argarray = new ArrayList<String>(); 166 for (String arg : args) { 167 if (arg.contains("background") || arg.startsWith("-b")) { 168 continue; 169 } else { 170 argarray.add(arg); 171 } 172 } 173 argarray.add("--background"); 174 argarray.add("false"); 175 int launchTimeout = serverOptions.getLaunchTimeout(); 176 LaunchUtil.relaunchAsBackgroundProcess(launchTimeout, argarray.toArray(new String[argarray.size()]), 177 processName); 178 setServerState(ServerState.STARTED_BACKGROUND); 179 // just in case 180 Thread.sleep(200); 181 System.exit(0); 182 } 183 184 // if the war is archived, unpack it to system temp 185 if(warFile.exists() && !warFile.isDirectory()) { 186 URL zipResource = warFile.toURI().toURL(); 187 String warDir = warFile.getName().toLowerCase().replace(".war", ""); 188 warFile = new File(warFile.getParentFile(), warDir); 189 if(!warFile.exists()) { 190 warFile.mkdir(); 191 log.debug("Exploding compressed WAR to " + warFile.getAbsolutePath()); 192 LaunchUtil.unzipResource(zipResource, warFile, false); 193 } else { 194 log.debug("Using already exploded WAR in " + warFile.getAbsolutePath()); 195 } 196 warPath = warFile.getAbsolutePath(); 197 if(serverOptions.getWarFile().getAbsolutePath().equals(serverOptions.getCfmlDirs())) { 198 serverOptions.setCfmlDirs(warFile.getAbsolutePath()); 199 } 200 } 201 202 tee = null; 203 if (serverOptions.getLogDir() != null) { 204 File logDirectory = serverOptions.getLogDir(); 205 logDirectory.mkdir(); 206 File outLog = new File(logDirectory,"server.out.txt"); 207 if (logDirectory.exists()) { 208 if(outLog.exists()) { 209 if(Files.size(Paths.get(outLog.getPath())) > 10 * 1024 * 1024) { 210 log.info("Log is over 10MB, moving " + outLog.getPath() + " to " + outLog.getPath() + ".bak"); 211 Files.move(Paths.get(outLog.getPath()), Paths.get(outLog.getPath()+".bak"), REPLACE_EXISTING); 212 } 213 } 214 log.info("Logging to " + outLog.getPath()); 215 tee = new TeeOutputStream(System.out, new FileOutputStream(outLog.getPath(), outLog.exists())); 216 PrintStream newOut = new PrintStream(tee, true); 217 System.setOut(newOut); 218 System.setErr(newOut); 219 } else { 220 log.error("Could not create log: " + outLog.getPath()); 221 } 222 } 223 224 new AgentInitialization().loadAgentFromLocalJarFile(new File(warFile, "/WEB-INF/lib/")); 225 226 String osName = System.getProperties().getProperty("os.name"); 227 String iconPNG = System.getProperty("cfml.server.trayicon"); 228 if( iconPNG != null && iconPNG.length() > 0) { 229 serverOptions.setIconImage(iconPNG); 230 } 231 String dockIconPath = System.getProperty("cfml.server.dockicon"); 232 if( dockIconPath == null || dockIconPath.length() == 0) { 233 dockIconPath = serverOptions.getIconImage(); 234 } 235 236 if (osName != null && osName.startsWith("Mac OS X")) { 237 Image dockIcon = LaunchUtil.getIconImage(dockIconPath); 238 System.setProperty("com.apple.mrj.application.apple.menu.about.name", processName); 239 System.setProperty("com.apple.mrj.application.growbox.intrudes", "false"); 240 System.setProperty("apple.laf.useScreenMenuBar", "true"); 241 System.setProperty("-Xdock:name", processName); 242 try { 243 Class<?> appClass = Class.forName("com.apple.eawt.Application"); 244 Method getAppMethod = appClass.getMethod("getApplication"); 245 Object appInstance = getAppMethod.invoke(null); 246 Method dockMethod = appInstance.getClass().getMethod("setDockIconImage", java.awt.Image.class); 247 dockMethod.invoke(appInstance, dockIcon); 248 } catch (Exception e) { 249 log.warn(e); 250 } 251 } 252 String startingtext = "Starting - port:" + portNumber + " stop-port:" + socketNumber + " warpath:" + warPath; 253 startingtext += "\ncontext: " + contextPath + " - version: " + getVersion(); 254 String cfmlDirs = serverOptions.getCfmlDirs(); 255 if (cfmlDirs.length() > 0) { 256 startingtext += "\nweb-dirs: " + cfmlDirs; 257 } 258 startingtext += "\nLog Directory: " + serverOptions.getLogDir().getAbsolutePath(); 259 System.out.println(bar); 260 System.out.println(startingtext); 261 //System.out.println("background: " + background); 262 System.out.println(bar); 263 addShutDownHook(); 264 portNumber = getPortOrErrorOut(portNumber, host); 265 socketNumber = getPortOrErrorOut(socketNumber, host); 266 String cfmlServletConfigWebDir = serverOptions.getCFMLServletConfigWebDir(); 267 String cfmlServletConfigServerDir = serverOptions.getCFMLServletConfigServerDir(); 268 File webXmlFile = serverOptions.getWebXmlFile(); 269 File webinf = new File(warFile, "WEB-INF"); 270 if (webXmlFile != null && new File(webXmlFile.getParentFile(), "lib").exists()) { 271 webinf = webXmlFile.getParentFile(); 272 } 273 String libDirs = serverOptions.getLibDirs(); 274 URL jarURL = serverOptions.getJarURL(); 275 if (warFile.isDirectory() && webinf.exists()) { 276 libDirs = webinf.getAbsolutePath() + "/lib"; 277 log.info("Using existing WEB-INF/lib of: " + libDirs); 278 } 279 280 List<URL> cp = new ArrayList<URL>(); 281 if (libDirs != null || jarURL != null) { 282 if (libDirs != null) 283 cp.addAll(getJarList(libDirs)); 284 if (jarURL != null) 285 cp.add(jarURL); 286 } 287 if(serverOptions.getMariaDB4jImportSQLFile() != null){ 288 System.out.println("ADDN"+serverOptions.getMariaDB4jImportSQLFile().toURI().toURL()); 289 cp.add(serverOptions.getMariaDB4jImportSQLFile().toURI().toURL()); 290 } 291 cp.addAll(getClassesList(new File(webinf, "/classes"))); 292 initClassLoader(cp); 293 294 mariadb4jManager = new MariaDB4jManager(_classLoader); 295 296 log.debug("Transfer Min Size: " + serverOptions.getTransferMinSize()); 297 298 final DeploymentInfo servletBuilder = deployment() 299 .setContextPath(contextPath.equals("/") ? "" : contextPath) 300 .setTempDir(new File(System.getProperty("java.io.tmpdir"))) 301 .setDeploymentName(warPath); 302 303 if (!warFile.exists()) { 304 throw new RuntimeException("war does not exist: " + warFile.getAbsolutePath()); 305 } 306 307 // hack to prevent . being picked up as the system path (jacob.x.dll) 308 if (System.getProperty("java.library.path") == null) { 309 if (webXmlFile != null) { 310 System.setProperty("java.library.path", getThisJarLocation().getPath() 311 + ':' + new File(webXmlFile.getParentFile(), "lib").getPath()); 312 } else { 313 System.setProperty("java.library.path", getThisJarLocation().getPath() 314 + ':' + new File(warFile, "/WEB-INF/lib/").getPath()); 315 } 316 } else { 317 System.setProperty("java.library.path", 318 getThisJarLocation().getPath() + ":" + System.getProperty("java.library.path")); 319 } 320 log.debug("java.library.path:" + System.getProperty("java.library.path")); 321 322 if (System.getProperty("coldfusion.home") == null) { 323 String cfusionDir = new File(webinf,"cfusion").getAbsolutePath(); 324 if (webXmlFile != null) { 325 cfusionDir = new File(webXmlFile.getParentFile(),"cfusion").getAbsolutePath(); 326 } 327 log.debug("Setting coldfusion home:" + cfusionDir); 328 System.setProperty("coldfusion.home", cfusionDir); 329 System.setProperty("coldfusion.rootDir", cfusionDir); 330// System.setProperty("javax.servlet.context.tempdir", cfusionDir + "/../cfclasses"); 331 System.setProperty("coldfusion.libPath", cfusionDir + "/lib"); 332 System.setProperty("flex.dir", new File(webinf,"cfform").getAbsolutePath()); 333 System.setProperty("coldfusion.jsafe.defaultalgo", "FIPS186Random"); 334 System.setProperty("coldfusion.classPath", cfusionDir + "/lib/updates/," + cfusionDir + "/lib/," 335 + cfusionDir + "/lib/axis2,"+ cfusionDir + "/gateway/lib/,"+ cfusionDir + "/../cfform/jars," 336 + cfusionDir + "/../flex/jars,"+ cfusionDir + "/lib/oosdk/lib,"+ cfusionDir + "/lib/oosdk/classes"); 337 System.setProperty("java.security.policy", cfusionDir + "/lib/coldfusion.policy"); 338 System.setProperty("java.security.auth.policy", cfusionDir + "/lib/neo_jaas.policy"); 339 System.setProperty("java.nixlibrary.path", cfusionDir + "/lib"); 340 System.setProperty("java.library.path", cfusionDir + "/lib"); 341 } 342 343 if(warFile.isDirectory() && !webinf.exists()) { 344 if (cfmlServletConfigWebDir == null) { 345 File webConfigDirFile = new File(getThisJarLocation().getParentFile(), "engine/cfml/server/cfml-web/"); 346 cfmlServletConfigWebDir = webConfigDirFile.getPath() + "/" + serverName; 347 } 348 log.debug("cfml.web.config.dir: " + cfmlServletConfigWebDir); 349 if (cfmlServletConfigServerDir == null || cfmlServletConfigServerDir.length() == 0) { 350 File serverConfigDirFile = new File(getThisJarLocation().getParentFile(), "engine/cfml/server/"); 351 cfmlServletConfigServerDir = serverConfigDirFile.getAbsolutePath(); 352 } 353 log.debug("cfml.server.config.dir: " + cfmlServletConfigServerDir); 354 String webinfDir = System.getProperty("cfml.webinf"); 355 if (webinfDir == null) { 356 webinf = new File(cfmlServletConfigWebDir, "WEB-INF/"); 357 } else { 358 webinf = new File(webinfDir); 359 } 360 log.debug("cfml.webinf: " + webinf.getPath()); 361 362 // servletBuilder.setResourceManager(new CFMLResourceManager(new 363 // File(homeDir,"server/"), transferMinSize, cfmlDirs)); 364 File internalCFMLServerRoot = webinf; 365 internalCFMLServerRoot.mkdirs(); 366 servletBuilder.setResourceManager(new MappedResourceManager(warFile, transferMinSize, cfmlDirs, internalCFMLServerRoot)); 367 368 if (webXmlFile != null) { 369 log.debug("using specified web.xml : " + webXmlFile.getAbsolutePath()); 370 servletBuilder.setClassLoader(_classLoader); 371 WebXMLParser.parseWebXml(webXmlFile, servletBuilder); 372 } else { 373 if (_classLoader == null) { 374 throw new RuntimeException("FATAL: Could not load any libs for war: " + warFile.getAbsolutePath()); 375 } 376 servletBuilder.setClassLoader(_classLoader); 377 Class cfmlServlet; 378 Class restServlet; 379 try { 380 cfmlServlet = _classLoader.loadClass(cfengine + ".loader.servlet.CFMLServlet"); 381 log.debug("dynamically loaded CFML servlet from runwar child classloader"); 382 } catch (java.lang.ClassNotFoundException e) { 383 cfmlServlet = Server.class.getClassLoader().loadClass(cfengine + ".loader.servlet.CFMLServlet"); 384 log.debug("dynamically loaded CFML servlet from runwar classloader"); 385 } 386 try { 387 restServlet = _classLoader.loadClass(cfengine + ".loader.servlet.RestServlet"); 388 } catch (java.lang.ClassNotFoundException e) { 389 restServlet = Server.class.getClassLoader().loadClass(cfengine + ".loader.servlet.RestServlet"); 390 } 391 log.debug("loaded servlet classes"); 392 servletBuilder 393 .addWelcomePages(serverOptions.getWelcomeFiles()) 394 .addServlets( 395 servlet("CFMLServlet", cfmlServlet) 396 .setRequireWelcomeFileMapping(true) 397 .addInitParam("configuration",cfmlServletConfigWebDir) 398 .addInitParam(cfengine+"-server-root",cfmlServletConfigServerDir) 399 .addMapping("*.cfm") 400 .addMapping("*.cfc") 401 .addMapping("/index.cfc/*") 402 .addMapping("/index.cfm/*") 403 .addMapping("/index.cfml/*") 404 .setLoadOnStartup(1) 405 , 406 servlet("RESTServlet", restServlet) 407 .setRequireWelcomeFileMapping(true) 408 .addInitParam(cfengine+"-web-directory",cfmlServletConfigWebDir) 409 .addMapping("/rest/*") 410 .setLoadOnStartup(2)); 411 } 412 } else if(webinf.exists()) { 413 log.debug("found WEB-INF: " + webinf.getAbsolutePath()); 414 if (_classLoader == null) { 415 throw new RuntimeException("FATAL: Could not load any libs for war: " + warFile.getAbsolutePath()); 416 } 417 servletBuilder.setClassLoader(_classLoader); 418 servletBuilder.setResourceManager(new MappedResourceManager(warFile, transferMinSize, cfmlDirs, webinf)); 419 LogSubverter.subvertJDKLoggers(loglevel); 420 WebXMLParser.parseWebXml(new File(webinf, "/web.xml"), servletBuilder); 421 } else { 422 throw new RuntimeException("Didn't know how to handle war:"+warFile.getAbsolutePath()); 423 } 424 /* 425 servletBuilder.addInitialHandlerChainWrapper(new HandlerWrapper() { 426 @Override 427 public HttpHandler wrap(final HttpHandler handler) { 428 return resource(new FileResourceManager(new File(libDir,"server/WEB-INF"), transferMinSize)) 429 .setDirectoryListingEnabled(true); 430 } 431 }); 432 */ 433 434 configureURLRewrite(servletBuilder, webinf); 435 436 if (serverOptions.isCacheEnabled()) { 437 addCacheHandler(servletBuilder); 438 } 439 440 if (serverOptions.isCustomHTTPStatusEnabled()) { 441 servletBuilder.setSendCustomReasonPhraseOnError(true); 442 } 443 444 // this prevents us from having to use our own ResourceHandler (directory listing, welcome files, see below) and error handler for now 445 servletBuilder.addServlet(new ServletInfo(io.undertow.servlet.handlers.ServletPathMatches.DEFAULT_SERVLET_NAME, DefaultServlet.class) 446 .addInitParam("directory-listing", Boolean.toString(serverOptions.isDirectoryListingEnabled()))); 447 448 manager = defaultContainer().addDeployment(servletBuilder); 449 450 manager.deploy(); 451 HttpHandler servletHandler = manager.start(); 452 log.debug("started servlet deployment manager"); 453/* 454 List welcomePages = manager.getDeployment().getDeploymentInfo().getWelcomePages(); 455 CFMLResourceHandler resourceHandler = new CFMLResourceHandler(servletBuilder.getResourceManager(), servletHandler, welcomePages); 456 resourceHandler.setDirectoryListingEnabled(directoryListingEnabled); 457 PathHandler pathHandler = Handlers.path(Handlers.redirect(contextPath)) 458 .addPrefixPath(contextPath, resourceHandler); 459 HttpHandler errPageHandler = new SimpleErrorPageHandler(pathHandler); 460 Builder serverBuilder = Undertow.builder().addHttpListener(portNumber, host).setHandler(errPageHandler); 461*/ 462 Builder serverBuilder = Undertow.builder(); 463 464 if(serverOptions.isEnableHTTP()) { 465 serverBuilder.addHttpListener(portNumber, host); 466 } 467 468 if (serverOptions.isEnableSSL()) { 469 int sslPort = serverOptions.getSSLPort(); 470 serverBuilder.setDirectBuffers(true); 471 log.info("Enabling SSL protocol on port " + sslPort); 472 if (serverOptions.getSSLCertificate() != null) { 473 File certfile = serverOptions.getSSLCertificate(); 474 File keyfile = serverOptions.getSSLKey(); 475 char[] keypass = serverOptions.getSSLKeyPass(); 476 serverBuilder.addHttpsListener(sslPort, host, SSLUtil.createSSLContext(certfile, keyfile, keypass)); 477 Arrays.fill(keypass, '*'); 478 } else { 479 serverBuilder.addHttpsListener(sslPort, host, SSLUtil.createSSLContext()); 480 } 481 } 482 483 if (serverOptions.isEnableAJP()) { 484 log.info("Enabling AJP protocol on port " + serverOptions.getAJPPort()); 485 serverBuilder.addAjpListener(serverOptions.getAJPPort(), host); 486 } 487 488// final PathHandler pathHandler = Handlers.path(Handlers.redirect(contextPath)) 489// .addPrefixPath(contextPath, servletHandler); 490 491 final PathHandler pathHandler = new PathHandler(Handlers.redirect(contextPath)) { 492 @Override 493 public void handleRequest(final HttpServerExchange exchange) throws Exception { 494 if (exchange.getRequestPath().endsWith(".svgz")) { 495 exchange.getResponseHeaders().put(Headers.CONTENT_ENCODING, "gzip"); 496 } 497 super.handleRequest(exchange); 498 } 499 }; 500 pathHandler.addPrefixPath(contextPath, servletHandler); 501 502// SessionManager sessionManager = new InMemorySessionManager("SESSION_MANAGER"); 503// SessionCookieConfig sessionConfig = new SessionCookieConfig(); 504// SessionAttachmentHandler sessionAttachmentHandler = new SessionAttachmentHandler(sessionManager, sessionConfig); 505// // set as next handler your root handler 506// sessionAttachmentHandler.setNext(pathHandler); 507 508 509 if (serverOptions.isGzipEnabled()) { 510 final EncodingHandler handler = new EncodingHandler(new ContentEncodingRepository().addEncodingHandler( 511 "gzip", new GzipEncodingProvider(), 50, Predicates.parse("max-content-size[5]"))) 512 .setNext(pathHandler); 513 serverBuilder.setHandler(handler); 514 } else { 515 serverBuilder.setHandler(pathHandler); 516 } 517 518 try { 519 PID = ManagementFactory.getRuntimeMXBean().getName().split("@")[0]; 520 String pidFile = serverOptions.getPidFile(); 521 if (pidFile != null && pidFile.length() > 0) { 522 File file = new File(pidFile); 523 file.deleteOnExit(); 524 PrintWriter writer = new PrintWriter(file); 525 writer.print(PID); 526 writer.close(); 527 } 528 } catch (Exception e) { 529 log.error("Unable to get PID:" + e.getMessage()); 530 } 531 if (serverOptions.isKeepRequestLog()) { 532 log.error("request log currently unsupported"); 533 } 534 535 // start the stop monitor thread 536 undertow = serverBuilder.build(); 537 Thread monitor = new MonitorThread(stoppassword); 538 monitor.start(); 539 log.debug("started stop monitor"); 540 LaunchUtil.hookTray(this); 541 log.debug("hooked system tray"); 542 543 if (serverOptions.isOpenbrowser()) { 544 new Server(3); 545 } 546 547 // if this println changes be sure to update the LaunchUtil so it will know success 548 String msg = "Server is up - http-port:" + portNumber + " stop-port:" + socketNumber +" PID:" + PID + " version " + getVersion(); 549 log.debug(msg); 550 System.out.println(msg); 551 setServerState(ServerState.STARTED); 552 553 if (serverOptions.isMariaDB4jEnabled()) { 554 try { 555 mariadb4jManager.start(serverOptions.getMariaDB4jPort(), serverOptions.getMariaDB4jBaseDir(), 556 serverOptions.getMariaDB4jDataDir(), serverOptions.getMariaDB4jImportSQLFile()); 557 } catch (Exception dbStartException) { 558 log.error("Could not start MariaDB4j"); 559 log.error(dbStartException); 560 System.out.println("Error starting MariaDB4j: " + dbStartException.getMessage()); 561 } 562 } 563 564 undertow.start(); 565 } 566 567 private void addShutDownHook() { 568 final Thread mainThread = Thread.currentThread(); 569 Runtime.getRuntime().addShutdownHook(new Thread() { 570 public void run() { 571 try { 572 stopServer(); 573// if(tempWarDir != null) { 574// LaunchUtil.deleteRecursive(tempWarDir); 575// } 576 mainThread.join(); 577 } catch ( Exception e) { 578 // TODO Auto-generated catch block 579 e.printStackTrace(); 580 } 581 } 582 }); 583 log.debug("Added shutdown hook"); 584 } 585 586 public void stopServer() { 587 int exitCode = 0; 588 try{ 589 System.out.println(); 590 System.out.println(bar); 591 System.out.println("*** stopping server"); 592 if (serverOptions.isMariaDB4jEnabled()) { 593 mariadb4jManager.stop(); 594 } 595 try { 596 manager.undeploy(); 597 undertow.stop(); 598 Thread.sleep(1000); 599 } catch (Exception notRunning) { 600 System.out.println("*** server did not appear to be running"); 601 } 602 System.out.println(bar); 603 setServerState(ServerState.STOPPED); 604 } catch (Exception e) { 605 e.printStackTrace(); 606 setServerState(ServerState.UNKNOWN); 607 log.error(e); 608 exitCode = 1; 609 } 610 try { 611 if (tee != null) 612 tee.close(); 613 } catch (Exception e) { 614 System.out.println("Redirect: Unable to close this log file!"); 615 } 616 if(exitCode != 0) { 617 System.exit(exitCode); 618 } 619 } 620 621 622 @SuppressWarnings({ "rawtypes", "unchecked" }) 623 private void configureURLRewrite(DeploymentInfo servletBuilder, File webInfDir) throws ClassNotFoundException, IOException { 624 if(serverOptions.isEnableURLRewrite()) { 625 log.debug("enabling URL rewriting"); 626 Class rewriteFilter; 627 String urlRewriteFile = "runwar/urlrewrite.xml"; 628 try{ 629 rewriteFilter = _classLoader.loadClass("org.tuckey.web.filters.urlrewrite.UrlRewriteFilter"); 630 } catch (java.lang.ClassNotFoundException e) { 631 rewriteFilter = Server.class.getClassLoader().loadClass("org.tuckey.web.filters.urlrewrite.UrlRewriteFilter"); 632 } 633 if(serverOptions.getURLRewriteFile() != null) { 634 if(!serverOptions.getURLRewriteFile().isFile()) { 635 log.error("The URL rewrite file " + urlRewriteFile + " does not exist!"); 636 } else { 637 String rewriteFileName = "urlrewrite.xml"; 638 LaunchUtil.copyFile(serverOptions.getURLRewriteFile(), new File(webInfDir, rewriteFileName)); 639 log.debug("Copying URL rewrite file " + serverOptions.getURLRewriteFile().getPath() + " to WEB-INF: " + webInfDir.getPath() + "/"+rewriteFileName); 640 urlRewriteFile = "/WEB-INF/"+rewriteFileName; 641 } 642 } 643 log.debug("URL rewriting config file: " + urlRewriteFile); 644 servletBuilder.addFilter(new FilterInfo("UrlRewriteFilter", rewriteFilter) 645 .addInitParam("confPath", urlRewriteFile) 646 .addInitParam("statusEnabled", Boolean.toString(serverOptions.isDebug())) 647 .addInitParam("modRewriteConf", "false")); 648 servletBuilder.addFilterUrlMapping("UrlRewriteFilter", "/*", DispatcherType.REQUEST); 649 } else { 650 log.debug("URL rewriting is disabled"); 651 } 652 } 653 654 private void addCacheHandler(final DeploymentInfo servletBuilder) { 655 // this handles mime types and adds a simple cache for static files 656 servletBuilder.addInitialHandlerChainWrapper(new HandlerWrapper() { 657 @Override 658 public HttpHandler wrap(final HttpHandler handler) { 659 final ResourceHandler resourceHandler = new ResourceHandler(servletBuilder.getResourceManager()); 660 io.undertow.util.MimeMappings.Builder mimes = MimeMappings.builder(); 661 List<String> suffixList = new ArrayList<String>(); 662 // add font mime types not included by default 663 mimes.addMapping("eot", "application/vnd.ms-fontobject"); 664 mimes.addMapping("otf", "font/opentype"); 665 mimes.addMapping("ttf", "application/x-font-ttf"); 666 mimes.addMapping("woff", "application/x-font-woff"); 667 suffixList.addAll(Arrays.asList(".eot",".otf",".ttf",".woff")); 668 // add the default types and any added in web.xml files 669 for(MimeMapping mime : servletBuilder.getMimeMappings()) { 670 log.debug("Adding mime-type: " + mime.getExtension() + " - " + mime.getMimeType()); 671 mimes.addMapping(mime.getExtension(), mime.getMimeType()); 672 suffixList.add("."+mime.getExtension()); 673 } 674 resourceHandler.setMimeMappings(mimes.build()); 675 String[] suffixes = new String[suffixList.size()]; 676 suffixes = suffixList.toArray(suffixes); 677 // simple cacheHandler, someday maybe make this configurable 678 final CacheHandler cacheHandler = new CacheHandler(new DirectBufferCache(1024, 10, 10480), resourceHandler); 679 final PredicateHandler predicateHandler = new PredicateHandler(Predicates.suffixes(suffixes), cacheHandler, handler); 680 return predicateHandler; 681 } 682 }); 683 } 684 685 public static File getThisJarLocation() { 686 return LaunchUtil.getJarDir(Server.class); 687 } 688 689 public String getPID() { 690 return PID; 691 } 692 693 private int getPortOrErrorOut(int portNumber, String host) { 694 try { 695 ServerSocket nextAvail = new ServerSocket(portNumber, 1, InetAddress.getByName(host)); 696 portNumber = nextAvail.getLocalPort(); 697 nextAvail.close(); 698 return portNumber; 699 } catch (java.net.BindException e) { 700 throw new RuntimeException("Error getting port " + portNumber + "! Cannot start. " + e.getMessage()); 701 } catch (UnknownHostException e) { 702 throw new RuntimeException("Unknown host (" + host + ")"); 703 } catch (IOException e) { 704 throw new RuntimeException(e); 705 } 706 } 707 708 private List<URL> getJarList(String libDirs) throws IOException { 709 List<URL> classpath = new ArrayList<URL>(); 710 String[] list = libDirs.split(","); 711 if (list == null) 712 return classpath; 713 714 for (String path : list) { 715 if (".".equals(path) || "..".equals(path)) 716 continue; 717 718 File file = new File(path); 719 for (File item : file.listFiles()) { 720 String fileName = item.getAbsolutePath(); 721 if (!item.isDirectory()) { 722 if (fileName.toLowerCase().endsWith(".jar") || fileName.toLowerCase().endsWith(".zip")) { 723 URL url = item.toURI().toURL(); 724 classpath.add(url); 725 log.debug("lib: added to classpath: " + fileName); 726 } 727 } 728 } 729 } 730 return classpath; 731 } 732 733 private List<URL> getClassesList(File classesDir) throws IOException { 734 List<URL> classpath = new ArrayList<URL>(); 735 if (classesDir == null) 736 return classpath; 737 if (classesDir.exists() && classesDir.isDirectory()) { 738 URL url = classesDir.toURI().toURL(); 739 classpath.add(url); 740 for (File item : classesDir.listFiles()) { 741 if (item.isDirectory()) { 742 classpath.addAll(getClassesList(item)); 743 } 744 } 745 } else { 746 log.debug("WEB-INF classes directory (" + classesDir.getAbsolutePath() + ") does not exist"); 747 } 748 return classpath; 749 } 750 751 public static void printVersion() { 752 System.out.println(LaunchUtil.getResourceAsString("runwar/version.properties")); 753 System.out.println(LaunchUtil.getResourceAsString("io/undertow/version.properties")); 754 } 755 756 private static String getVersion() { 757 String[] version = LaunchUtil.getResourceAsString("runwar/version.properties").split("="); 758 return version[version.length - 1].trim(); 759 } 760 761 private class MonitorThread extends Thread { 762 763 private char[] stoppassword; 764 765 public MonitorThread(char[] stoppassword) { 766 this.stoppassword = stoppassword; 767 setDaemon(true); 768 setName("StopMonitor"); 769 } 770 771 @Override 772 public void run() { 773 // Executor exe = Executors.newCachedThreadPool(); 774 ServerSocket serverSocket = null; 775 int exitCode = 0; 776 listening = true; 777 try { 778 serverSocket = new ServerSocket(socketNumber, 1, InetAddress.getByName(serverOptions.getHost())); 779 System.out.println(bar); 780 System.out.println("*** starting 'stop' listener thread - Host: " + serverOptions.getHost() 781 + " - Socket: " + socketNumber); 782 System.out.println(bar); 783 while (listening) { 784 final Socket clientSocket = serverSocket.accept(); 785 int r, i = 0; 786 BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); 787 try { 788 while ((r = reader.read()) != -1) { 789 char ch = (char) r; 790 if (stoppassword.length > i && ch == stoppassword[i]) { 791 i++; 792 } else { 793 i = 0; // prevent prefix only matches 794 } 795 } 796 if (i == stoppassword.length) { 797 listening = false; 798 } else { 799 log.warn("Incorrect password used when trying to stop server."); 800 } 801 } catch (java.net.SocketException e) { 802 // reset 803 } 804 try { 805 clientSocket.close(); 806 } catch (IOException e) { 807 e.printStackTrace(); 808 } 809 } 810 serverSocket.close(); 811 } catch (Exception e) { 812 exitCode = 1; 813 e.printStackTrace(); 814 } 815// stopServer(); 816 System.exit(exitCode); 817// Thread.currentThread().interrupt(); 818 return; 819 } 820 } 821 822 public static boolean serverWentDown(int timeout, long sleepTime, InetAddress server, int port) { 823 long start = System.currentTimeMillis(); 824 while ((System.currentTimeMillis() - start) < timeout) { 825 if (checkServerIsUp(server, port)) { 826 try { 827 Thread.sleep(sleepTime); 828 } catch (InterruptedException e) { 829 return false; 830 } 831 } else { 832 return true; 833 } 834 } 835 return false; 836 } 837 838 public static boolean serverCameUp(int timeout, long sleepTime, InetAddress server, int port) { 839 long start = System.currentTimeMillis(); 840 while ((System.currentTimeMillis() - start) < timeout) { 841 if (!checkServerIsUp(server, port)) { 842 try { 843 Thread.sleep(sleepTime); 844 } catch (InterruptedException e) { 845 return false; 846 } 847 } else { 848 return true; 849 } 850 } 851 return false; 852 } 853 854 public static boolean checkServerIsUp(InetAddress server, int port) { 855 Socket sock = null; 856 try { 857 sock = SocketFactory.getDefault().createSocket(server, port); 858 sock.setSoLinger(true, 0); 859 return true; 860 } catch (IOException e) { 861 return false; 862 } finally { 863 if (sock != null) { 864 try { 865 sock.close(); 866 } catch (IOException e) { 867 // don't care 868 } 869 } 870 } 871 } 872 873 class OpenBrowserTask extends TimerTask { 874 public void run() { 875 int portNumber = serverOptions.getPortNumber(); 876 String protocol = "http"; 877 String host = serverOptions.getHost(); 878 String openbrowserURL = serverOptions.getOpenbrowserURL(); 879 int timeout = serverOptions.getLaunchTimeout(); 880 if (openbrowserURL == null || openbrowserURL.length() == 0) { 881 openbrowserURL = "http://" + host + ":" + portNumber; 882 } 883 if(serverOptions.isEnableSSL()) { 884 portNumber = serverOptions.getSSLPort(); 885 protocol = "https"; 886 openbrowserURL = openbrowserURL.replace("http:", "https:"); 887 } 888 if (!openbrowserURL.startsWith("http")) { 889 openbrowserURL = (!openbrowserURL.startsWith("/")) ? "/" + openbrowserURL : openbrowserURL; 890 openbrowserURL = protocol + "://" + host + ":" + portNumber + openbrowserURL; 891 } 892 System.out.println("Waiting up to " + (timeout/1000) + " seconds for " + host + ":" + portNumber + "..."); 893 try { 894 if (serverCameUp(timeout, 3000, InetAddress.getByName(host), portNumber)) { 895 System.out.println("Opening browser to..." + openbrowserURL); 896 BrowserOpener.openURL(openbrowserURL.trim()); 897 } else { 898 System.out.println("could not open browser to..." + openbrowserURL + "... timeout..."); 899 } 900 } catch (Exception e) { 901 log.error(e.getMessage()); 902 } 903 return; 904 } 905 } 906 907 public static ServerOptions getServerOptions() { 908 return serverOptions; 909 } 910 911 private void setServerState(String state) { 912 serverState = state; 913 if (statusFile != null) { 914 try { 915 PrintWriter writer; 916 writer = new PrintWriter(statusFile); 917 writer.print(state); 918 writer.close(); 919 } catch (FileNotFoundException e) { 920 log.error(e.getMessage()); 921 } 922 } 923 } 924 925 public String getServerState() { 926 return serverState; 927 } 928 929 public static class ServerState { 930 public static final String STARTING = "STARTING"; 931 public static final String STARTED = "STARTED"; 932 public static final String STARTING_BACKGROUND = "STARTING_BACKGROUND"; 933 public static final String STARTED_BACKGROUND = "STARTED_BACKGROUND"; 934 public static final String STOPPING = "STOPPING"; 935 public static final String STOPPED = "STOPPED"; 936 public static final String UNKNOWN = "UNKNOWN"; 937 } 938 939}