aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnton Tananaev <anton.tananaev@gmail.com>2018-09-14 16:46:40 +1200
committerAnton Tananaev <anton.tananaev@gmail.com>2018-09-14 16:46:40 +1200
commit5d819b2776328e914a26ae011692084e1b614e26 (patch)
tree6b535ea62ca0651a6ae2f6a88f88863f7af24eca
parentb10824bb5769c4abb93e3451673b88efdfb9d467 (diff)
downloadtrackermap-server-5d819b2776328e914a26ae011692084e1b614e26.tar.gz
trackermap-server-5d819b2776328e914a26ae011692084e1b614e26.tar.bz2
trackermap-server-5d819b2776328e914a26ae011692084e1b614e26.zip
Add Windows service support
-rw-r--r--pom.xml5
-rw-r--r--src/org/traccar/Context.java8
-rw-r--r--src/org/traccar/Main.java24
-rw-r--r--src/org/traccar/WindowsService.java216
4 files changed, 246 insertions, 7 deletions
diff --git a/pom.xml b/pom.xml
index 8d864cc66..8c4f52573 100644
--- a/pom.xml
+++ b/pom.xml
@@ -174,6 +174,11 @@
<artifactId>ch-smpp</artifactId>
<version>6.0.0-netty4-beta-3</version>
</dependency>
+ <dependency>
+ <groupId>net.java.dev.jna</groupId>
+ <artifactId>jna-platform</artifactId>
+ <version>4.5.2</version>
+ </dependency>
</dependencies>
<build>
diff --git a/src/org/traccar/Context.java b/src/org/traccar/Context.java
index 9a8bb97a9..f7bc18d1d 100644
--- a/src/org/traccar/Context.java
+++ b/src/org/traccar/Context.java
@@ -340,14 +340,10 @@ public final class Context {
}
}
- public static void init(String[] arguments) throws Exception {
+ public static void init(String configFile) throws Exception {
config = new Config();
- if (arguments.length <= 0) {
- throw new RuntimeException("Configuration file is not provided");
- }
-
- config.load(arguments[0]);
+ config.load(configFile);
loggerEnabled = config.getBoolean("logger.enable");
if (loggerEnabled) {
diff --git a/src/org/traccar/Main.java b/src/org/traccar/Main.java
index fd1f07dca..986731240 100644
--- a/src/org/traccar/Main.java
+++ b/src/org/traccar/Main.java
@@ -67,7 +67,29 @@ public final class Main {
public static void main(String[] args) throws Exception {
Locale.setDefault(Locale.ENGLISH);
- Context.init(args);
+ if (args.length <= 0) {
+ throw new RuntimeException("Configuration file is not provided");
+ }
+
+ String configFile = args[args.length - 1];
+
+ if (args.length > 1) {
+ WindowsService windowsService = new WindowsService("traccar");
+ switch (args[1]) {
+ case "--install":
+ windowsService.install("traccar", null, null, null, null, configFile);
+ return;
+ case "--uninstall":
+ windowsService.uninstall();
+ return;
+ case "--service":
+ default:
+ windowsService.init();
+ break;
+ }
+ }
+
+ Context.init(configFile);
logSystemInfo();
LOGGER.info("Version: " + Context.getAppVersion());
LOGGER.info("Starting server...");
diff --git a/src/org/traccar/WindowsService.java b/src/org/traccar/WindowsService.java
new file mode 100644
index 000000000..398648b7b
--- /dev/null
+++ b/src/org/traccar/WindowsService.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * 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 org.traccar;
+
+import com.sun.jna.Pointer;
+import com.sun.jna.platform.win32.Advapi32;
+import com.sun.jna.platform.win32.WinError;
+import com.sun.jna.platform.win32.WinNT;
+import com.sun.jna.platform.win32.Winsvc;
+import com.sun.jna.platform.win32.Winsvc.HandlerEx;
+import com.sun.jna.platform.win32.Winsvc.SC_HANDLE;
+import com.sun.jna.platform.win32.Winsvc.SERVICE_DESCRIPTION;
+import com.sun.jna.platform.win32.Winsvc.SERVICE_MAIN_FUNCTION;
+import com.sun.jna.platform.win32.Winsvc.SERVICE_STATUS;
+import com.sun.jna.platform.win32.Winsvc.SERVICE_STATUS_HANDLE;
+import com.sun.jna.platform.win32.Winsvc.SERVICE_TABLE_ENTRY;
+import java.io.File;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLClassLoader;
+
+public class WindowsService {
+
+ private static final Advapi32 ADVAPI_32 = Advapi32.INSTANCE;
+
+ private final Object waitObject = new Object();
+
+ private String serviceName;
+ private ServiceMain serviceMain;
+ private ServiceControl serviceControl;
+ private SERVICE_STATUS_HANDLE serviceStatusHandle;
+
+ public WindowsService(String serviceName) {
+ this.serviceName = serviceName;
+ }
+
+ public boolean install(
+ String displayName, String description, String[] dependencies,
+ String account, String password, String config) throws URISyntaxException {
+
+ String javaHome = System.getProperty("java.home");
+ String javaBinary = javaHome + "\\bin\\java.exe";
+ URLClassLoader cl = (URLClassLoader) WindowsService.class.getClassLoader();
+ URL jarPath = cl.getURLs()[0];
+
+ File jar = new File(jarPath.toURI());
+ String command = javaBinary + " -jar \"" + jar.getAbsolutePath() + "\" --service \"" + config + "\"";
+
+ boolean success = false;
+ StringBuilder dep = new StringBuilder();
+
+ if (dependencies != null) {
+ for (String s : dependencies) {
+ dep.append(s);
+ dep.append("\0");
+ }
+ }
+ dep.append("\0");
+
+ SERVICE_DESCRIPTION desc = new SERVICE_DESCRIPTION();
+ desc.lpDescription = description;
+
+ SC_HANDLE serviceManager = openServiceControlManager(null, Winsvc.SC_MANAGER_ALL_ACCESS);
+
+ if (serviceManager != null) {
+ SC_HANDLE service = ADVAPI_32.CreateService(serviceManager, serviceName, displayName,
+ Winsvc.SERVICE_ALL_ACCESS, WinNT.SERVICE_WIN32_OWN_PROCESS, WinNT.SERVICE_AUTO_START,
+ WinNT.SERVICE_ERROR_NORMAL,
+ command,
+ null, null, dep.toString(), account, password);
+
+ if (service != null) {
+ success = ADVAPI_32.ChangeServiceConfig2(service, Winsvc.SERVICE_CONFIG_DESCRIPTION, desc);
+ ADVAPI_32.CloseServiceHandle(service);
+ }
+ ADVAPI_32.CloseServiceHandle(serviceManager);
+ }
+ return success;
+ }
+
+ public boolean uninstall() {
+ boolean success = false;
+
+ SC_HANDLE serviceManager = openServiceControlManager(null, Winsvc.SC_MANAGER_ALL_ACCESS);
+
+ if (serviceManager != null) {
+ SC_HANDLE service = ADVAPI_32.OpenService(serviceManager, serviceName, Winsvc.SERVICE_ALL_ACCESS);
+
+ if (service != null) {
+ success = ADVAPI_32.DeleteService(service);
+ ADVAPI_32.CloseServiceHandle(service);
+ }
+ ADVAPI_32.CloseServiceHandle(serviceManager);
+ }
+ return success;
+ }
+
+ public boolean start() {
+ boolean success = false;
+
+ SC_HANDLE serviceManager = openServiceControlManager(null, WinNT.GENERIC_EXECUTE);
+
+ if (serviceManager != null) {
+ SC_HANDLE service = ADVAPI_32.OpenService(serviceManager, serviceName, WinNT.GENERIC_EXECUTE);
+
+ if (service != null) {
+ success = ADVAPI_32.StartService(service, 0, null);
+ ADVAPI_32.CloseServiceHandle(service);
+ }
+ ADVAPI_32.CloseServiceHandle(serviceManager);
+ }
+
+ return success;
+ }
+
+ public boolean stop() {
+ boolean success = false;
+
+ SC_HANDLE serviceManager = openServiceControlManager(null, WinNT.GENERIC_EXECUTE);
+
+ if (serviceManager != null) {
+ SC_HANDLE service = Advapi32.INSTANCE.OpenService(serviceManager, serviceName, WinNT.GENERIC_EXECUTE);
+
+ if (service != null) {
+ SERVICE_STATUS serviceStatus = new SERVICE_STATUS();
+ success = Advapi32.INSTANCE.ControlService(service, Winsvc.SERVICE_CONTROL_STOP, serviceStatus);
+ Advapi32.INSTANCE.CloseServiceHandle(service);
+ }
+ Advapi32.INSTANCE.CloseServiceHandle(serviceManager);
+ }
+
+ return (success);
+ }
+
+ public void init() {
+ serviceMain = new ServiceMain();
+ SERVICE_TABLE_ENTRY entry = new SERVICE_TABLE_ENTRY();
+ entry.lpServiceName = serviceName;
+ entry.lpServiceProc = serviceMain;
+
+ Advapi32.INSTANCE.StartServiceCtrlDispatcher((SERVICE_TABLE_ENTRY[]) entry.toArray(2));
+ }
+
+ private SC_HANDLE openServiceControlManager(String machine, int access) {
+ return ADVAPI_32.OpenSCManager(machine, null, access);
+ }
+
+ private void reportStatus(int status, int win32ExitCode, int waitHint) {
+ SERVICE_STATUS serviceStatus = new SERVICE_STATUS();
+ serviceStatus.dwServiceType = WinNT.SERVICE_WIN32_OWN_PROCESS;
+ serviceStatus.dwControlsAccepted = Winsvc.SERVICE_ACCEPT_STOP | Winsvc.SERVICE_ACCEPT_SHUTDOWN;
+ serviceStatus.dwWin32ExitCode = win32ExitCode;
+ serviceStatus.dwWaitHint = waitHint;
+ serviceStatus.dwCurrentState = status;
+
+ ADVAPI_32.SetServiceStatus(serviceStatusHandle, serviceStatus);
+ }
+
+ private class ServiceMain implements SERVICE_MAIN_FUNCTION {
+
+ public void callback(int dwArgc, Pointer lpszArgv) {
+ serviceControl = new ServiceControl();
+ serviceStatusHandle = ADVAPI_32.RegisterServiceCtrlHandlerEx(serviceName, serviceControl, null);
+
+ reportStatus(Winsvc.SERVICE_START_PENDING, WinError.NO_ERROR, 3000);
+ reportStatus(Winsvc.SERVICE_RUNNING, WinError.NO_ERROR, 0);
+
+ try {
+ synchronized (waitObject) {
+ waitObject.wait();
+ }
+ } catch (InterruptedException ex) {
+ }
+ reportStatus(Winsvc.SERVICE_STOPPED, WinError.NO_ERROR, 0);
+
+ // Avoid returning from ServiceMain, which will cause a crash
+ // See http://support.microsoft.com/kb/201349, which recommends
+ // having init() wait for this thread.
+ // Waiting on this thread in init() won't fix the crash, though.
+ }
+
+ }
+
+ private class ServiceControl implements HandlerEx {
+
+ public int callback(int dwControl, int dwEventType, Pointer lpEventData, Pointer lpContext) {
+ switch (dwControl) {
+ case Winsvc.SERVICE_CONTROL_STOP:
+ case Winsvc.SERVICE_CONTROL_SHUTDOWN:
+ reportStatus(Winsvc.SERVICE_STOP_PENDING, WinError.NO_ERROR, 5000);
+ System.exit(0);
+ synchronized (waitObject) {
+ waitObject.notifyAll();
+ }
+ default:
+ break;
+ }
+ return WinError.NO_ERROR;
+ }
+
+ }
+
+}