diff options
Diffstat (limited to 'src/main/java/org/traccar/schedule')
5 files changed, 183 insertions, 22 deletions
diff --git a/src/main/java/org/traccar/schedule/ScheduleManager.java b/src/main/java/org/traccar/schedule/ScheduleManager.java index 5d5054100..6412a186a 100644 --- a/src/main/java/org/traccar/schedule/ScheduleManager.java +++ b/src/main/java/org/traccar/schedule/ScheduleManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2020 - 2022 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. @@ -15,29 +15,39 @@ */ package org.traccar.schedule; +import com.google.inject.Injector; +import org.traccar.LifecycleObject; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; -public class ScheduleManager { +@Singleton +public class ScheduleManager implements LifecycleObject { + private final Injector injector; private ScheduledExecutorService executor; - public void start() { + @Inject + public ScheduleManager(Injector injector) { + this.injector = injector; + } + @Override + public void start() { executor = Executors.newSingleThreadScheduledExecutor(); - - new TaskDeviceInactivityCheck().schedule(executor); - new TaskWebSocketKeepalive().schedule(executor); - + List.of(TaskDeviceInactivityCheck.class, TaskWebSocketKeepalive.class, TaskHealthCheck.class) + .forEach(task -> injector.getInstance(task).schedule(executor)); } + @Override public void stop() { - if (executor != null) { executor.shutdown(); executor = null; } - } } diff --git a/src/main/java/org/traccar/schedule/ScheduleTask.java b/src/main/java/org/traccar/schedule/ScheduleTask.java new file mode 100644 index 000000000..1b537213b --- /dev/null +++ b/src/main/java/org/traccar/schedule/ScheduleTask.java @@ -0,0 +1,22 @@ +/* + * Copyright 2022 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.schedule; + +import java.util.concurrent.ScheduledExecutorService; + +public interface ScheduleTask extends Runnable { + void schedule(ScheduledExecutorService executor); +} diff --git a/src/main/java/org/traccar/schedule/TaskDeviceInactivityCheck.java b/src/main/java/org/traccar/schedule/TaskDeviceInactivityCheck.java index 80641d7d4..57c64dc5b 100644 --- a/src/main/java/org/traccar/schedule/TaskDeviceInactivityCheck.java +++ b/src/main/java/org/traccar/schedule/TaskDeviceInactivityCheck.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2020 - 2022 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. @@ -15,17 +15,26 @@ */ package org.traccar.schedule; -import org.traccar.Context; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traccar.database.NotificationManager; import org.traccar.model.Device; import org.traccar.model.Event; import org.traccar.model.Position; +import org.traccar.storage.Storage; +import org.traccar.storage.StorageException; +import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Request; +import javax.inject.Inject; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -public class TaskDeviceInactivityCheck implements Runnable { +public class TaskDeviceInactivityCheck implements ScheduleTask { + + private static final Logger LOGGER = LoggerFactory.getLogger(TaskDeviceInactivityCheck.class); public static final String ATTRIBUTE_DEVICE_INACTIVITY_START = "deviceInactivityStart"; public static final String ATTRIBUTE_DEVICE_INACTIVITY_PERIOD = "deviceInactivityPeriod"; @@ -33,6 +42,16 @@ public class TaskDeviceInactivityCheck implements Runnable { private static final long CHECK_PERIOD_MINUTES = 15; + private final Storage storage; + private final NotificationManager notificationManager; + + @Inject + public TaskDeviceInactivityCheck(Storage storage, NotificationManager notificationManager) { + this.storage = storage; + this.notificationManager = notificationManager; + } + + @Override public void schedule(ScheduledExecutorService executor) { executor.scheduleAtFixedRate(this, CHECK_PERIOD_MINUTES, CHECK_PERIOD_MINUTES, TimeUnit.MINUTES); } @@ -43,15 +62,20 @@ public class TaskDeviceInactivityCheck implements Runnable { long checkPeriod = TimeUnit.MINUTES.toMillis(CHECK_PERIOD_MINUTES); Map<Event, Position> events = new HashMap<>(); - for (Device device : Context.getDeviceManager().getAllDevices()) { - if (device.getLastUpdate() != null && checkDevice(device, currentTime, checkPeriod)) { - Event event = new Event(Event.TYPE_DEVICE_INACTIVE, device.getId()); - event.set(ATTRIBUTE_LAST_UPDATE, device.getLastUpdate().getTime()); - events.put(event, null); + + try { + for (Device device : storage.getObjects(Device.class, new Request(new Columns.All()))) { + if (device.getLastUpdate() != null && checkDevice(device, currentTime, checkPeriod)) { + Event event = new Event(Event.TYPE_DEVICE_INACTIVE, device.getId()); + event.set(ATTRIBUTE_LAST_UPDATE, device.getLastUpdate().getTime()); + events.put(event, null); + } } + } catch (StorageException e) { + LOGGER.warn("Get devices error", e); } - Context.getNotificationManager().updateEvents(events); + notificationManager.updateEvents(events); } private boolean checkDevice(Device device, long currentTime, long checkPeriod) { diff --git a/src/main/java/org/traccar/schedule/TaskHealthCheck.java b/src/main/java/org/traccar/schedule/TaskHealthCheck.java new file mode 100644 index 000000000..a8c9873ce --- /dev/null +++ b/src/main/java/org/traccar/schedule/TaskHealthCheck.java @@ -0,0 +1,96 @@ +/* + * Copyright 2020 - 2022 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.schedule; + +import com.sun.jna.Library; +import com.sun.jna.Native; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traccar.config.Config; +import org.traccar.config.Keys; + +import javax.inject.Inject; +import javax.ws.rs.client.Client; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +public class TaskHealthCheck implements ScheduleTask { + + private static final Logger LOGGER = LoggerFactory.getLogger(TaskHealthCheck.class); + + private final Config config; + private final Client client; + + private SystemD systemD; + + private boolean enabled; + private long period; + + @Inject + public TaskHealthCheck(Config config, Client client) { + this.config = config; + this.client = client; + if (!config.getBoolean(Keys.WEB_DISABLE_HEALTH_CHECK) + && System.getProperty("os.name").toLowerCase().startsWith("linux")) { + try { + systemD = Native.load("systemd", SystemD.class); + String watchdogTimer = System.getenv("WATCHDOG_USEC"); + if (watchdogTimer != null && !watchdogTimer.isEmpty()) { + period = Long.parseLong(watchdogTimer) / 1000 * 4 / 5; + } + if (period > 0) { + LOGGER.info("Health check enabled with period {}", period); + enabled = true; + } + } catch (UnsatisfiedLinkError e) { + LOGGER.warn("No systemd support", e); + } + } + } + + private String getUrl() { + String address = config.getString(Keys.WEB_ADDRESS, "localhost"); + int port = config.getInteger(Keys.WEB_PORT); + return "http://" + address + ":" + port + "/api/server"; + } + + @Override + public void schedule(ScheduledExecutorService executor) { + if (enabled) { + executor.scheduleAtFixedRate(this, period, period, TimeUnit.MILLISECONDS); + } + } + + @Override + public void run() { + LOGGER.debug("Health check running"); + int status = client.target(getUrl()).request().get().getStatus(); + if (status == 200) { + int result = systemD.sd_notify(0, "WATCHDOG=1"); + if (result < 0) { + LOGGER.warn("Health check notify error {}", result); + } + } else { + LOGGER.warn("Health check failed with status {}", status); + } + } + + interface SystemD extends Library { + @SuppressWarnings("checkstyle:MethodName") + int sd_notify(@SuppressWarnings("checkstyle:ParameterName") int unset_environment, String state); + } + +} diff --git a/src/main/java/org/traccar/schedule/TaskWebSocketKeepalive.java b/src/main/java/org/traccar/schedule/TaskWebSocketKeepalive.java index 953b0efea..e6c2e8b6d 100644 --- a/src/main/java/org/traccar/schedule/TaskWebSocketKeepalive.java +++ b/src/main/java/org/traccar/schedule/TaskWebSocketKeepalive.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2021 - 2022 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. @@ -15,22 +15,31 @@ */ package org.traccar.schedule; -import org.traccar.Context; +import org.traccar.session.ConnectionManager; +import javax.inject.Inject; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -public class TaskWebSocketKeepalive implements Runnable { +public class TaskWebSocketKeepalive implements ScheduleTask { private static final long PERIOD_SECONDS = 55; + private final ConnectionManager connectionManager; + + @Inject + public TaskWebSocketKeepalive(ConnectionManager connectionManager) { + this.connectionManager = connectionManager; + } + + @Override public void schedule(ScheduledExecutorService executor) { executor.scheduleAtFixedRate(this, PERIOD_SECONDS, PERIOD_SECONDS, TimeUnit.SECONDS); } @Override public void run() { - Context.getConnectionManager().sendKeepalive(); + connectionManager.sendKeepalive(); } } |