From b6ba5a8ac60e8013e22ed44b15ffdc5ad8097060 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Fri, 17 Jul 2015 14:32:55 +1200 Subject: Rename package from http to web --- src/org/traccar/Context.java | 2 +- src/org/traccar/database/DataManager.java | 4 +- src/org/traccar/http/AsyncServlet.java | 211 --------------------------- src/org/traccar/http/BaseServlet.java | 91 ------------ src/org/traccar/http/CommandsServlet.java | 58 -------- src/org/traccar/http/DeviceServlet.java | 75 ---------- src/org/traccar/http/EnumFactory.java | 20 --- src/org/traccar/http/JsonConverter.java | 155 -------------------- src/org/traccar/http/JsonIgnore.java | 12 -- src/org/traccar/http/MainServlet.java | 80 ---------- src/org/traccar/http/PositionServlet.java | 47 ------ src/org/traccar/http/ServerServlet.java | 53 ------- src/org/traccar/http/UserServlet.java | 81 ---------- src/org/traccar/http/WebServer.java | 116 --------------- src/org/traccar/model/User.java | 2 +- src/org/traccar/web/AsyncServlet.java | 211 +++++++++++++++++++++++++++ src/org/traccar/web/BaseServlet.java | 91 ++++++++++++ src/org/traccar/web/CommandsServlet.java | 58 ++++++++ src/org/traccar/web/DeviceServlet.java | 75 ++++++++++ src/org/traccar/web/EnumFactory.java | 20 +++ src/org/traccar/web/JsonConverter.java | 155 ++++++++++++++++++++ src/org/traccar/web/JsonIgnore.java | 12 ++ src/org/traccar/web/MainServlet.java | 80 ++++++++++ src/org/traccar/web/PositionServlet.java | 47 ++++++ src/org/traccar/web/ServerServlet.java | 53 +++++++ src/org/traccar/web/UserServlet.java | 81 ++++++++++ src/org/traccar/web/WebServer.java | 116 +++++++++++++++ test/org/traccar/http/JsonConverterTest.java | 171 ---------------------- test/org/traccar/web/JsonConverterTest.java | 170 +++++++++++++++++++++ 29 files changed, 1173 insertions(+), 1174 deletions(-) delete mode 100644 src/org/traccar/http/AsyncServlet.java delete mode 100644 src/org/traccar/http/BaseServlet.java delete mode 100644 src/org/traccar/http/CommandsServlet.java delete mode 100644 src/org/traccar/http/DeviceServlet.java delete mode 100644 src/org/traccar/http/EnumFactory.java delete mode 100644 src/org/traccar/http/JsonConverter.java delete mode 100644 src/org/traccar/http/JsonIgnore.java delete mode 100644 src/org/traccar/http/MainServlet.java delete mode 100644 src/org/traccar/http/PositionServlet.java delete mode 100644 src/org/traccar/http/ServerServlet.java delete mode 100644 src/org/traccar/http/UserServlet.java delete mode 100644 src/org/traccar/http/WebServer.java create mode 100644 src/org/traccar/web/AsyncServlet.java create mode 100644 src/org/traccar/web/BaseServlet.java create mode 100644 src/org/traccar/web/CommandsServlet.java create mode 100644 src/org/traccar/web/DeviceServlet.java create mode 100644 src/org/traccar/web/EnumFactory.java create mode 100644 src/org/traccar/web/JsonConverter.java create mode 100644 src/org/traccar/web/JsonIgnore.java create mode 100644 src/org/traccar/web/MainServlet.java create mode 100644 src/org/traccar/web/PositionServlet.java create mode 100644 src/org/traccar/web/ServerServlet.java create mode 100644 src/org/traccar/web/UserServlet.java create mode 100644 src/org/traccar/web/WebServer.java delete mode 100644 test/org/traccar/http/JsonConverterTest.java create mode 100644 test/org/traccar/web/JsonConverterTest.java diff --git a/src/org/traccar/Context.java b/src/org/traccar/Context.java index 4e6055174..d50a0c626 100644 --- a/src/org/traccar/Context.java +++ b/src/org/traccar/Context.java @@ -24,7 +24,7 @@ import org.traccar.geocode.GoogleReverseGeocoder; import org.traccar.geocode.NominatimReverseGeocoder; import org.traccar.geocode.ReverseGeocoder; import org.traccar.helper.Log; -import org.traccar.http.WebServer; +import org.traccar.web.WebServer; public class Context { diff --git a/src/org/traccar/database/DataManager.java b/src/org/traccar/database/DataManager.java index 04695c4a5..f68ff2ae6 100644 --- a/src/org/traccar/database/DataManager.java +++ b/src/org/traccar/database/DataManager.java @@ -34,8 +34,8 @@ import javax.sql.DataSource; import org.traccar.Config; import org.traccar.helper.DriverDelegate; import org.traccar.helper.Log; -import org.traccar.http.AsyncServlet; -import org.traccar.http.JsonConverter; +import org.traccar.web.AsyncServlet; +import org.traccar.web.JsonConverter; import org.traccar.model.Device; import org.traccar.model.MiscFormatter; import org.traccar.model.Permission; diff --git a/src/org/traccar/http/AsyncServlet.java b/src/org/traccar/http/AsyncServlet.java deleted file mode 100644 index 0fc1cd2fb..000000000 --- a/src/org/traccar/http/AsyncServlet.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) - * - * 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.http; - -import java.io.IOException; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import javax.json.Json; -import javax.json.JsonObjectBuilder; -import javax.servlet.AsyncContext; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.jboss.netty.util.Timeout; -import org.jboss.netty.util.TimerTask; -import org.traccar.Context; -import org.traccar.GlobalTimer; -import org.traccar.database.ConnectionManager; -import org.traccar.helper.Log; -import org.traccar.model.Position; - -public class AsyncServlet extends BaseServlet { - - private static final long ASYNC_TIMEOUT = 120000; - - @Override - protected boolean handle(String command, HttpServletRequest req, HttpServletResponse resp) throws Exception { - async(req.startAsync(), getUserId(req)); - return true; - } - - public class AsyncSession { - - private static final boolean DEBUG_ASYNC = false; - - private static final long SESSION_TIMEOUT = 30; - private static final long REQUEST_TIMEOUT = 20; - - private boolean destroyed; - private final long userId; - private final Set devices = new HashSet<>(); - private Timeout sessionTimeout; - private Timeout requestTimeout; - private final Map positions = new HashMap<>(); - private AsyncContext activeContext; - - private void logEvent(String message) { - if (DEBUG_ASYNC) { - Log.debug("AsyncSession: " + this.hashCode() + " destroyed: " + destroyed + " " + message); - } - } - - public AsyncSession(long userId, Collection devices) { - logEvent("create userId: " + userId + " devices: " + devices.size()); - this.userId = userId; - this.devices.addAll(devices); - - Collection initialPositions = Context.getConnectionManager().getInitialState(devices); - for (Position position : initialPositions) { - positions.put(position.getDeviceId(), position); - } - - Context.getConnectionManager().addListener(devices, dataListener); - } - - public boolean hasDevice(long deviceId) { - return devices.contains(deviceId); - } - - private final ConnectionManager.DataCacheListener dataListener = new ConnectionManager.DataCacheListener() { - @Override - public void onUpdate(Position position) { - synchronized (AsyncSession.this) { - logEvent("onUpdate deviceId: " + position.getDeviceId()); - if (!destroyed) { - if (requestTimeout != null) { - requestTimeout.cancel(); - requestTimeout = null; - } - positions.put(position.getDeviceId(), position); - if (activeContext != null) { - response(); - } - } - } - } - }; - - private final TimerTask sessionTimer = new TimerTask() { - @Override - public void run(Timeout tmt) throws Exception { - synchronized (AsyncSession.this) { - logEvent("sessionTimeout"); - Context.getConnectionManager().removeListener(devices, dataListener); - synchronized (asyncSessions) { - asyncSessions.remove(userId); - } - destroyed = true; - } - } - }; - - private final TimerTask requestTimer = new TimerTask() { - @Override - public void run(Timeout tmt) throws Exception { - synchronized (AsyncSession.this) { - logEvent("requestTimeout"); - if (!destroyed) { - if (activeContext != null) { - response(); - } - } - } - } - }; - - public synchronized void request(AsyncContext context) { - logEvent("request context: " + context.hashCode()); - if (!destroyed) { - activeContext = context; - if (sessionTimeout != null) { - sessionTimeout.cancel(); - sessionTimeout = null; - } - - if (!positions.isEmpty()) { - response(); - } else { - requestTimeout = GlobalTimer.getTimer().newTimeout( - requestTimer, REQUEST_TIMEOUT, TimeUnit.SECONDS); - } - } - } - - private synchronized void response() { - logEvent("response context: " + activeContext.hashCode()); - if (!destroyed) { - ServletResponse response = activeContext.getResponse(); - - JsonObjectBuilder result = Json.createObjectBuilder(); - result.add("success", true); - result.add("data", JsonConverter.arrayToJson(positions.values())); - positions.clear(); - - try { - response.getWriter().println(result.build().toString()); - } catch (IOException error) { - Log.warning(error); - } - - activeContext.complete(); - activeContext = null; - - sessionTimeout = GlobalTimer.getTimer().newTimeout( - sessionTimer, SESSION_TIMEOUT, TimeUnit.SECONDS); - } - } - - } - - private static final Map asyncSessions = new HashMap<>(); - - public static void sessionRefreshUser(long userId) { - asyncSessions.remove(userId); - } - - public static void sessionRefreshDevice(long deviceId) { - Iterator> iterator = asyncSessions.entrySet().iterator(); - while (iterator.hasNext()) { - if (iterator.next().getValue().hasDevice(deviceId)) { - iterator.remove(); - } - } - } - - private void async(final AsyncContext context, long userId) { - - context.setTimeout(ASYNC_TIMEOUT); - HttpServletRequest req = (HttpServletRequest) context.getRequest(); - - synchronized (asyncSessions) { - - if (Boolean.valueOf(req.getParameter("first")) || !asyncSessions.containsKey(userId)) { - Collection devices = Context.getPermissionsManager().allowedDevices(userId); - asyncSessions.put(userId, new AsyncSession(userId, devices)); - } - - asyncSessions.get(userId).request(context); - } - } - -} diff --git a/src/org/traccar/http/BaseServlet.java b/src/org/traccar/http/BaseServlet.java deleted file mode 100644 index 394e4d317..000000000 --- a/src/org/traccar/http/BaseServlet.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) - * - * 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.http; - -import java.io.IOException; -import java.io.Writer; -import java.security.AccessControlException; -import javax.json.Json; -import javax.json.JsonObjectBuilder; -import javax.json.JsonStructure; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.traccar.model.User; - -public abstract class BaseServlet extends HttpServlet { - - public static final String USER_KEY = "user"; - - @Override - protected final void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - String command = req.getPathInfo(); - if (command == null) { - command = ""; - } - try { - resp.setContentType("application/json"); - resp.setCharacterEncoding("UTF-8"); - if (!handle(command, req, resp)) { - resp.sendError(HttpServletResponse.SC_BAD_REQUEST); - } - } catch (Exception error) { - sendResponse(resp.getWriter(), error); - } - } - - protected abstract boolean handle(String command, HttpServletRequest req, HttpServletResponse resp) throws Exception; - - public long getUserId(HttpServletRequest req) { - User user = (User) req.getSession().getAttribute(USER_KEY); - if (user == null) { - throw new AccessControlException("User not logged in"); - } - return user.getId(); - } - - public void securityCheck(boolean check) throws SecurityException { - if (!check) { - throw new SecurityException("Access denied"); - } - } - - public void sendResponse(Writer writer, boolean success) throws IOException { - JsonObjectBuilder result = Json.createObjectBuilder(); - result.add("success", success); - writer.write(result.build().toString()); - } - - public void sendResponse(Writer writer, JsonStructure json) throws IOException { - JsonObjectBuilder result = Json.createObjectBuilder(); - result.add("success", true); - result.add("data", json); - writer.write(result.build().toString()); - } - - public void sendResponse(Writer writer, Exception error) throws IOException { - JsonObjectBuilder result = Json.createObjectBuilder(); - result.add("success", false); - if (error.getMessage() != null) { - result.add("error", error.getMessage()); - } else { - result.add("error", error.getClass().getSimpleName()); - } - writer.write(result.build().toString()); - } - -} diff --git a/src/org/traccar/http/CommandsServlet.java b/src/org/traccar/http/CommandsServlet.java deleted file mode 100644 index 8b3d43fe7..000000000 --- a/src/org/traccar/http/CommandsServlet.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.traccar.http; - -import javax.json.Json; -import javax.json.JsonObject; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.traccar.Context; -import org.traccar.command.CommandType; -import org.traccar.command.GpsCommand; -import org.traccar.database.ActiveDevice; - -public class CommandsServlet extends BaseServlet { - - @Override - protected boolean handle(String command, HttpServletRequest req, HttpServletResponse resp) throws Exception { - - switch (command) { - case "/send": - send(req, resp); - return true; - case "/raw": - sendRawCommand(req, resp); - return true; - default: - return false; - } - } - - private void send(HttpServletRequest req, HttpServletResponse resp) throws Exception { - GpsCommand command = JsonConverter.enumObjectFromJson(req.getReader(), new EnumFactory(CommandType.class, "type")); - - String uniqueId = command.getUniqueId(); - - ActiveDevice activeDevice = Context.getConnectionManager().getActiveDevice(uniqueId); - if(activeDevice == null) { - throw new RuntimeException("The device has not yet registered to the server"); - } - - activeDevice.sendCommand(command); - - sendResponse(resp.getWriter(), JsonConverter.objectToJson(new Object())); - } - - private void sendRawCommand(HttpServletRequest req, HttpServletResponse resp) throws Exception { - JsonObject json = Json.createReader(req.getReader()).readObject(); - String uniqueId = json.getString("uniqueId"); - - ActiveDevice activeDevice = Context.getConnectionManager().getActiveDevice(uniqueId); - if(activeDevice == null) { - throw new RuntimeException("The device has not yet registered to the server"); - } - - String command = json.getString("command"); - activeDevice.write(command); - - sendResponse(resp.getWriter(), JsonConverter.objectToJson(new Object())); - } -} diff --git a/src/org/traccar/http/DeviceServlet.java b/src/org/traccar/http/DeviceServlet.java deleted file mode 100644 index d1af1c106..000000000 --- a/src/org/traccar/http/DeviceServlet.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) - * - * 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.http; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.traccar.Context; -import org.traccar.model.Device; - -public class DeviceServlet extends BaseServlet { - - @Override - protected boolean handle(String command, HttpServletRequest req, HttpServletResponse resp) throws Exception { - - switch (command) { - case "/get": - get(req, resp); - break; - case "/add": - add(req, resp); - break; - case "/update": - update(req, resp); - break; - case "/remove": - remove(req, resp); - break; - default: - return false; - } - return true; - } - - private void get(HttpServletRequest req, HttpServletResponse resp) throws Exception { - sendResponse(resp.getWriter(), JsonConverter.arrayToJson( - Context.getDataManager().getDevices(getUserId(req)))); - } - - private void add(HttpServletRequest req, HttpServletResponse resp) throws Exception { - Device device = JsonConverter.objectFromJson(req.getReader(), new Device()); - Context.getDataManager().addDevice(device); - Context.getDataManager().linkDevice(getUserId(req), device.getId()); - Context.getPermissionsManager().refresh(); - sendResponse(resp.getWriter(), JsonConverter.objectToJson(device)); - } - - private void update(HttpServletRequest req, HttpServletResponse resp) throws Exception { - Device device = JsonConverter.objectFromJson(req.getReader(), new Device()); - Context.getPermissionsManager().checkDevice(getUserId(req), device.getId()); - Context.getDataManager().updateDevice(device); - sendResponse(resp.getWriter(), true); - } - - private void remove(HttpServletRequest req, HttpServletResponse resp) throws Exception { - Device device = JsonConverter.objectFromJson(req.getReader(), new Device()); - Context.getPermissionsManager().checkDevice(getUserId(req), device.getId()); - Context.getDataManager().removeDevice(device); - Context.getPermissionsManager().refresh(); - sendResponse(resp.getWriter(), true); - } - -} diff --git a/src/org/traccar/http/EnumFactory.java b/src/org/traccar/http/EnumFactory.java deleted file mode 100644 index 6e9971f71..000000000 --- a/src/org/traccar/http/EnumFactory.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.traccar.http; - -import org.traccar.model.Factory; - -import javax.json.JsonObject; - -public class EnumFactory & Factory> { - private Class commandTypeClass; - private String jsonKey; - - public EnumFactory(Class commandTypeClass, String type) { - this.commandTypeClass = commandTypeClass; - jsonKey = type; - } - - public K create(JsonObject json) { - Factory factory = Enum.valueOf(commandTypeClass, json.getString(jsonKey)); - return (K) factory.create(); - } -} diff --git a/src/org/traccar/http/JsonConverter.java b/src/org/traccar/http/JsonConverter.java deleted file mode 100644 index deb676f6e..000000000 --- a/src/org/traccar/http/JsonConverter.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) - * - * 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.http; - -import java.beans.Introspector; -import java.io.Reader; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Collection; -import java.util.Date; -import java.util.Map; -import javax.json.Json; -import javax.json.JsonArray; -import javax.json.JsonArrayBuilder; -import javax.json.JsonObject; -import javax.json.JsonObjectBuilder; -import javax.json.JsonValue; -import org.traccar.model.Factory; -import org.traccar.model.MiscFormatter; - -public class JsonConverter { - - private static final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); - - public static Date parseDate(String value) throws ParseException { - return dateFormat.parse(value); - } - - public static T objectFromJson(Reader reader, T prototype) throws ParseException { - return objectFromJson(Json.createReader(reader).readObject(), prototype); - } - - public static T enumObjectFromJson(Reader reader, EnumFactory> factory) throws ParseException { - JsonObject json = Json.createReader(reader).readObject(); - T object = factory.create(json); - populateObject(json, object); - return object; - } - - public static T objectFromJson(JsonObject json, T prototype) throws ParseException { - T object = (T) prototype.create(); - populateObject(json, object); - return object; - } - - private static void populateObject(JsonObject json, Object object) throws ParseException { - Method[] methods = object.getClass().getMethods(); - - for (final Method method : methods) { - if (method.getName().startsWith("set") && method.getParameterTypes().length == 1) { - - final String name = Introspector.decapitalize(method.getName().substring(3)); - Class parameterType = method.getParameterTypes()[0]; - - if (json.containsKey(name)) try { - if (parameterType.equals(boolean.class)) { - method.invoke(object, json.getBoolean(name)); - } else if (parameterType.equals(int.class)) { - method.invoke(object, json.getJsonNumber(name).intValue()); - } else if (parameterType.equals(long.class)) { - if (json.get(name).getValueType() == JsonValue.ValueType.NUMBER) { - method.invoke(object, json.getJsonNumber(name).longValue()); - } - } else if (parameterType.equals(double.class)) { - method.invoke(object, json.getJsonNumber(name).doubleValue()); - } else if (parameterType.equals(String.class)) { - method.invoke(object, json.getString(name)); - } else if (parameterType.equals(Date.class)) { - method.invoke(object, dateFormat.parse(json.getString(name))); - } else if (parameterType.isEnum()) { - method.invoke(object, Enum.valueOf((Class) parameterType, json.getString(name))); - } else if (parameterType.equals(Map.class)) { - //method.invoke(object, json.getString(name)); - } else { - Object nestedObject = parameterType.newInstance(); - populateObject(json.getJsonObject(name), nestedObject); - method.invoke(object, nestedObject); - } - } catch (IllegalAccessException | InvocationTargetException | InstantiationException error) { - } - } - } - - } - - public static JsonObject objectToJson(T object) { - - JsonObjectBuilder json = Json.createObjectBuilder(); - - Method[] methods = object.getClass().getMethods(); - - for (Method method : methods) { - if(method.isAnnotationPresent(JsonIgnore.class)) { - continue; - } - if (method.getName().startsWith("get") && method.getParameterTypes().length == 0) { - String name = Introspector.decapitalize(method.getName().substring(3)); - try { - if (method.getReturnType().equals(boolean.class)) { - json.add(name, (Boolean) method.invoke(object)); - } else if (method.getReturnType().equals(int.class)) { - json.add(name, (Integer) method.invoke(object)); - } else if (method.getReturnType().equals(long.class)) { - json.add(name, (Long) method.invoke(object)); - } else if (method.getReturnType().equals(double.class)) { - json.add(name, (Double) method.invoke(object)); - } else if (method.getReturnType().equals(String.class)) { - String value = (String) method.invoke(object); - if (value != null) { - json.add(name, value); - } - } else if (method.getReturnType().equals(Date.class)) { - Date value = (Date) method.invoke(object); - if (value != null) { - json.add(name, dateFormat.format(value)); - } - } else if (method.getReturnType().equals(Map.class)) { - json.add(name, MiscFormatter.toJson((Map) method.invoke(object))); - } - } catch (IllegalAccessException | InvocationTargetException error) { - } - } - } - - return json.build(); - } - - public static JsonArray arrayToJson(Collection array) { - - JsonArrayBuilder json = Json.createArrayBuilder(); - - for (Object object : array) { - json.add(objectToJson(object)); - } - - return json.build(); - } - -} diff --git a/src/org/traccar/http/JsonIgnore.java b/src/org/traccar/http/JsonIgnore.java deleted file mode 100644 index ad7791e0d..000000000 --- a/src/org/traccar/http/JsonIgnore.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.traccar.http; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import static java.lang.annotation.ElementType.METHOD; - -@Retention(RetentionPolicy.RUNTIME) -@Target(value = {METHOD}) -public @interface JsonIgnore { -} diff --git a/src/org/traccar/http/MainServlet.java b/src/org/traccar/http/MainServlet.java deleted file mode 100644 index 29eaaec33..000000000 --- a/src/org/traccar/http/MainServlet.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) - * - * 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.http; - -import java.io.IOException; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.traccar.Context; -import org.traccar.model.User; - -public class MainServlet extends BaseServlet { - - @Override - protected boolean handle(String command, HttpServletRequest req, HttpServletResponse resp) throws Exception { - - switch (command) { - case "/session": - session(req, resp); - break; - case "/login": - login(req, resp); - break; - case "/logout": - logout(req, resp); - break; - case "/register": - register(req, resp); - break; - default: - return false; - } - return true; - } - - private void session(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - User user = (User) req.getSession().getAttribute(USER_KEY); - if (user != null) { - sendResponse(resp.getWriter(), JsonConverter.objectToJson(user)); - } else { - sendResponse(resp.getWriter(), false); - } - } - - private void login(HttpServletRequest req, HttpServletResponse resp) throws Exception { - User user = Context.getDataManager().login( - req.getParameter("email"), req.getParameter("password")); - if (user != null) { - req.getSession().setAttribute(USER_KEY, user); - sendResponse(resp.getWriter(), JsonConverter.objectToJson(user)); - } else { - sendResponse(resp.getWriter(), false); - } - } - - private void logout(HttpServletRequest req, HttpServletResponse resp) throws Exception { - req.getSession().removeAttribute(USER_KEY); - sendResponse(resp.getWriter(), true); - } - - private void register(HttpServletRequest req, HttpServletResponse resp) throws Exception { - User user = JsonConverter.objectFromJson(req.getReader(), new User()); - Context.getDataManager().addUser(user); - sendResponse(resp.getWriter(), true); - } - -} diff --git a/src/org/traccar/http/PositionServlet.java b/src/org/traccar/http/PositionServlet.java deleted file mode 100644 index 6f973be3c..000000000 --- a/src/org/traccar/http/PositionServlet.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) - * - * 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.http; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.traccar.Context; - -public class PositionServlet extends BaseServlet { - - @Override - protected boolean handle(String command, HttpServletRequest req, HttpServletResponse resp) throws Exception { - - switch (command) { - case "/get": - get(req, resp); - break; - default: - return false; - } - return true; - } - - private void get(HttpServletRequest req, HttpServletResponse resp) throws Exception { - long deviceId = Long.valueOf(req.getParameter("deviceId")); - Context.getPermissionsManager().checkDevice(getUserId(req), deviceId); - sendResponse(resp.getWriter(), JsonConverter.arrayToJson( - Context.getDataManager().getPositions( - getUserId(req), deviceId, - JsonConverter.parseDate(req.getParameter("from")), - JsonConverter.parseDate(req.getParameter("to"))))); - } - -} diff --git a/src/org/traccar/http/ServerServlet.java b/src/org/traccar/http/ServerServlet.java deleted file mode 100644 index 836a109fc..000000000 --- a/src/org/traccar/http/ServerServlet.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) - * - * 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.http; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.traccar.Context; -import org.traccar.model.Server; - -public class ServerServlet extends BaseServlet { - - @Override - protected boolean handle(String command, HttpServletRequest req, HttpServletResponse resp) throws Exception { - - switch (command) { - case "/get": - get(req, resp); - break; - case "/update": - update(req, resp); - break; - default: - return false; - } - return true; - } - - private void get(HttpServletRequest req, HttpServletResponse resp) throws Exception { - sendResponse(resp.getWriter(), JsonConverter.objectToJson( - Context.getDataManager().getServer())); - } - - private void update(HttpServletRequest req, HttpServletResponse resp) throws Exception { - Server server = JsonConverter.objectFromJson(req.getReader(), new Server()); - Context.getPermissionsManager().checkAdmin(getUserId(req)); - Context.getDataManager().updateServer(server); - sendResponse(resp.getWriter(), true); - } - -} diff --git a/src/org/traccar/http/UserServlet.java b/src/org/traccar/http/UserServlet.java deleted file mode 100644 index 976a62890..000000000 --- a/src/org/traccar/http/UserServlet.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) - * - * 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.http; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.traccar.Context; -import org.traccar.model.User; - -public class UserServlet extends BaseServlet { - - @Override - protected boolean handle(String command, HttpServletRequest req, HttpServletResponse resp) throws Exception { - - switch (command) { - case "/get": - get(req, resp); - break; - case "/add": - add(req, resp); - break; - case "/update": - update(req, resp); - break; - case "/remove": - remove(req, resp); - break; - default: - return false; - } - return true; - } - - private void get(HttpServletRequest req, HttpServletResponse resp) throws Exception { - Context.getPermissionsManager().checkAdmin(getUserId(req)); - sendResponse(resp.getWriter(), JsonConverter.arrayToJson( - Context.getDataManager().getUsers())); - } - - private void add(HttpServletRequest req, HttpServletResponse resp) throws Exception { - User user = JsonConverter.objectFromJson(req.getReader(), new User()); - Context.getPermissionsManager().checkUser(getUserId(req), user.getId()); - Context.getDataManager().addUser(user); - Context.getPermissionsManager().refresh(); - sendResponse(resp.getWriter(), JsonConverter.objectToJson(user)); - } - - private void update(HttpServletRequest req, HttpServletResponse resp) throws Exception { - User user = JsonConverter.objectFromJson(req.getReader(), new User()); - if (user.getAdmin()) { - Context.getPermissionsManager().checkAdmin(getUserId(req)); - } else { - Context.getPermissionsManager().checkUser(getUserId(req), user.getId()); - } - Context.getDataManager().updateUser(user); - Context.getPermissionsManager().refresh(); - sendResponse(resp.getWriter(), true); - } - - private void remove(HttpServletRequest req, HttpServletResponse resp) throws Exception { - User user = JsonConverter.objectFromJson(req.getReader(), new User()); - Context.getPermissionsManager().checkUser(getUserId(req), user.getId()); - Context.getDataManager().removeUser(user); - Context.getPermissionsManager().refresh(); - sendResponse(resp.getWriter(), true); - } - -} diff --git a/src/org/traccar/http/WebServer.java b/src/org/traccar/http/WebServer.java deleted file mode 100644 index 785d817e8..000000000 --- a/src/org/traccar/http/WebServer.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2012 - 2015 Anton Tananaev (anton.tananaev@gmail.com) - * - * 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.http; - -import java.net.InetSocketAddress; -import javax.naming.InitialContext; -import javax.sql.DataSource; -import org.eclipse.jetty.server.Handler; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.handler.HandlerList; -import org.eclipse.jetty.server.handler.ResourceHandler; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.eclipse.jetty.webapp.WebAppContext; -import org.traccar.Config; -import org.traccar.helper.Log; - -/** - * Integrated HTTP server - */ -public class WebServer { - - private Server server; - - private void initServer(Config config) { - - String address = config.getString("web.address"); - int port = config.getInteger("web.port", 8082); - if (address == null) { - server = new Server(port); - } else { - server = new Server(new InetSocketAddress(address, port)); - } - } - - public WebServer(Config config, DataSource dataSource) { - - initServer(config); - - try { - javax.naming.Context context = new InitialContext(); - context.bind("java:/DefaultDS", dataSource); - } catch (Exception error) { - Log.warning(error); - } - - WebAppContext webapp = new WebAppContext(); - webapp.setContextPath("/"); - webapp.setWar(config.getString("web.application")); - server.setHandler(webapp); - } - - public WebServer(Config config) { - - initServer(config); - - ServletContextHandler servletHandler = new ServletContextHandler(ServletContextHandler.SESSIONS); - servletHandler.setContextPath("/api"); - servletHandler.addServlet(new ServletHolder(new AsyncServlet()), "/async/*"); - servletHandler.addServlet(new ServletHolder(new ServerServlet()), "/server/*"); - servletHandler.addServlet(new ServletHolder(new UserServlet()), "/user/*"); - servletHandler.addServlet(new ServletHolder(new DeviceServlet()), "/device/*"); - servletHandler.addServlet(new ServletHolder(new PositionServlet()), "/position/*"); - servletHandler.addServlet(new ServletHolder(new CommandsServlet()), "/commands/*"); - servletHandler.addServlet(new ServletHolder(new MainServlet()), "/*"); - - /*ResourceHandler mobileResourceHandler = new ResourceHandler(); - mobileResourceHandler.setResourceBase(properties.getProperty("web.mobile")); - mobileResourceHandler.setWelcomeFiles(new String[] {"index.html"}); - ContextHandler mobileContext = new ContextHandler("/m"); - mobileContext.setHandler(mobileResourceHandler);*/ - - ResourceHandler resourceHandler = new ResourceHandler(); - resourceHandler.setResourceBase(config.getString("web.path")); - if (config.getBoolean("web.debug")) { - resourceHandler.setWelcomeFiles(new String[] { "debug.html" }); - } else { - resourceHandler.setWelcomeFiles(new String[] { "release.html" }); - } - - HandlerList handlerList = new HandlerList(); - handlerList.setHandlers(new Handler[] {servletHandler, resourceHandler}); - - server.setHandler(handlerList); - } - - public void start() { - try { - server.start(); - } catch (Exception error) { - Log.warning(error); - } - } - - public void stop() { - try { - server.stop(); - } catch (Exception error) { - Log.warning(error); - } - } - -} diff --git a/src/org/traccar/model/User.java b/src/org/traccar/model/User.java index dccb4851e..af9d353c5 100644 --- a/src/org/traccar/model/User.java +++ b/src/org/traccar/model/User.java @@ -16,7 +16,7 @@ package org.traccar.model; import org.traccar.helper.Hashing; -import org.traccar.http.JsonIgnore; +import org.traccar.web.JsonIgnore; public class User implements Factory { diff --git a/src/org/traccar/web/AsyncServlet.java b/src/org/traccar/web/AsyncServlet.java new file mode 100644 index 000000000..20a71ec1c --- /dev/null +++ b/src/org/traccar/web/AsyncServlet.java @@ -0,0 +1,211 @@ +/* + * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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.web; + +import java.io.IOException; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import javax.json.Json; +import javax.json.JsonObjectBuilder; +import javax.servlet.AsyncContext; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.jboss.netty.util.Timeout; +import org.jboss.netty.util.TimerTask; +import org.traccar.Context; +import org.traccar.GlobalTimer; +import org.traccar.database.ConnectionManager; +import org.traccar.helper.Log; +import org.traccar.model.Position; + +public class AsyncServlet extends BaseServlet { + + private static final long ASYNC_TIMEOUT = 120000; + + @Override + protected boolean handle(String command, HttpServletRequest req, HttpServletResponse resp) throws Exception { + async(req.startAsync(), getUserId(req)); + return true; + } + + public class AsyncSession { + + private static final boolean DEBUG_ASYNC = false; + + private static final long SESSION_TIMEOUT = 30; + private static final long REQUEST_TIMEOUT = 20; + + private boolean destroyed; + private final long userId; + private final Set devices = new HashSet<>(); + private Timeout sessionTimeout; + private Timeout requestTimeout; + private final Map positions = new HashMap<>(); + private AsyncContext activeContext; + + private void logEvent(String message) { + if (DEBUG_ASYNC) { + Log.debug("AsyncSession: " + this.hashCode() + " destroyed: " + destroyed + " " + message); + } + } + + public AsyncSession(long userId, Collection devices) { + logEvent("create userId: " + userId + " devices: " + devices.size()); + this.userId = userId; + this.devices.addAll(devices); + + Collection initialPositions = Context.getConnectionManager().getInitialState(devices); + for (Position position : initialPositions) { + positions.put(position.getDeviceId(), position); + } + + Context.getConnectionManager().addListener(devices, dataListener); + } + + public boolean hasDevice(long deviceId) { + return devices.contains(deviceId); + } + + private final ConnectionManager.DataCacheListener dataListener = new ConnectionManager.DataCacheListener() { + @Override + public void onUpdate(Position position) { + synchronized (AsyncSession.this) { + logEvent("onUpdate deviceId: " + position.getDeviceId()); + if (!destroyed) { + if (requestTimeout != null) { + requestTimeout.cancel(); + requestTimeout = null; + } + positions.put(position.getDeviceId(), position); + if (activeContext != null) { + response(); + } + } + } + } + }; + + private final TimerTask sessionTimer = new TimerTask() { + @Override + public void run(Timeout tmt) throws Exception { + synchronized (AsyncSession.this) { + logEvent("sessionTimeout"); + Context.getConnectionManager().removeListener(devices, dataListener); + synchronized (asyncSessions) { + asyncSessions.remove(userId); + } + destroyed = true; + } + } + }; + + private final TimerTask requestTimer = new TimerTask() { + @Override + public void run(Timeout tmt) throws Exception { + synchronized (AsyncSession.this) { + logEvent("requestTimeout"); + if (!destroyed) { + if (activeContext != null) { + response(); + } + } + } + } + }; + + public synchronized void request(AsyncContext context) { + logEvent("request context: " + context.hashCode()); + if (!destroyed) { + activeContext = context; + if (sessionTimeout != null) { + sessionTimeout.cancel(); + sessionTimeout = null; + } + + if (!positions.isEmpty()) { + response(); + } else { + requestTimeout = GlobalTimer.getTimer().newTimeout( + requestTimer, REQUEST_TIMEOUT, TimeUnit.SECONDS); + } + } + } + + private synchronized void response() { + logEvent("response context: " + activeContext.hashCode()); + if (!destroyed) { + ServletResponse response = activeContext.getResponse(); + + JsonObjectBuilder result = Json.createObjectBuilder(); + result.add("success", true); + result.add("data", JsonConverter.arrayToJson(positions.values())); + positions.clear(); + + try { + response.getWriter().println(result.build().toString()); + } catch (IOException error) { + Log.warning(error); + } + + activeContext.complete(); + activeContext = null; + + sessionTimeout = GlobalTimer.getTimer().newTimeout( + sessionTimer, SESSION_TIMEOUT, TimeUnit.SECONDS); + } + } + + } + + private static final Map asyncSessions = new HashMap<>(); + + public static void sessionRefreshUser(long userId) { + asyncSessions.remove(userId); + } + + public static void sessionRefreshDevice(long deviceId) { + Iterator> iterator = asyncSessions.entrySet().iterator(); + while (iterator.hasNext()) { + if (iterator.next().getValue().hasDevice(deviceId)) { + iterator.remove(); + } + } + } + + private void async(final AsyncContext context, long userId) { + + context.setTimeout(ASYNC_TIMEOUT); + HttpServletRequest req = (HttpServletRequest) context.getRequest(); + + synchronized (asyncSessions) { + + if (Boolean.valueOf(req.getParameter("first")) || !asyncSessions.containsKey(userId)) { + Collection devices = Context.getPermissionsManager().allowedDevices(userId); + asyncSessions.put(userId, new AsyncSession(userId, devices)); + } + + asyncSessions.get(userId).request(context); + } + } + +} diff --git a/src/org/traccar/web/BaseServlet.java b/src/org/traccar/web/BaseServlet.java new file mode 100644 index 000000000..28b4b467e --- /dev/null +++ b/src/org/traccar/web/BaseServlet.java @@ -0,0 +1,91 @@ +/* + * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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.web; + +import java.io.IOException; +import java.io.Writer; +import java.security.AccessControlException; +import javax.json.Json; +import javax.json.JsonObjectBuilder; +import javax.json.JsonStructure; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.traccar.model.User; + +public abstract class BaseServlet extends HttpServlet { + + public static final String USER_KEY = "user"; + + @Override + protected final void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String command = req.getPathInfo(); + if (command == null) { + command = ""; + } + try { + resp.setContentType("application/json"); + resp.setCharacterEncoding("UTF-8"); + if (!handle(command, req, resp)) { + resp.sendError(HttpServletResponse.SC_BAD_REQUEST); + } + } catch (Exception error) { + sendResponse(resp.getWriter(), error); + } + } + + protected abstract boolean handle(String command, HttpServletRequest req, HttpServletResponse resp) throws Exception; + + public long getUserId(HttpServletRequest req) { + User user = (User) req.getSession().getAttribute(USER_KEY); + if (user == null) { + throw new AccessControlException("User not logged in"); + } + return user.getId(); + } + + public void securityCheck(boolean check) throws SecurityException { + if (!check) { + throw new SecurityException("Access denied"); + } + } + + public void sendResponse(Writer writer, boolean success) throws IOException { + JsonObjectBuilder result = Json.createObjectBuilder(); + result.add("success", success); + writer.write(result.build().toString()); + } + + public void sendResponse(Writer writer, JsonStructure json) throws IOException { + JsonObjectBuilder result = Json.createObjectBuilder(); + result.add("success", true); + result.add("data", json); + writer.write(result.build().toString()); + } + + public void sendResponse(Writer writer, Exception error) throws IOException { + JsonObjectBuilder result = Json.createObjectBuilder(); + result.add("success", false); + if (error.getMessage() != null) { + result.add("error", error.getMessage()); + } else { + result.add("error", error.getClass().getSimpleName()); + } + writer.write(result.build().toString()); + } + +} diff --git a/src/org/traccar/web/CommandsServlet.java b/src/org/traccar/web/CommandsServlet.java new file mode 100644 index 000000000..b12c0fa53 --- /dev/null +++ b/src/org/traccar/web/CommandsServlet.java @@ -0,0 +1,58 @@ +package org.traccar.web; + +import javax.json.Json; +import javax.json.JsonObject; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.traccar.Context; +import org.traccar.command.CommandType; +import org.traccar.command.GpsCommand; +import org.traccar.database.ActiveDevice; + +public class CommandsServlet extends BaseServlet { + + @Override + protected boolean handle(String command, HttpServletRequest req, HttpServletResponse resp) throws Exception { + + switch (command) { + case "/send": + send(req, resp); + return true; + case "/raw": + sendRawCommand(req, resp); + return true; + default: + return false; + } + } + + private void send(HttpServletRequest req, HttpServletResponse resp) throws Exception { + GpsCommand command = JsonConverter.enumObjectFromJson(req.getReader(), new EnumFactory(CommandType.class, "type")); + + String uniqueId = command.getUniqueId(); + + ActiveDevice activeDevice = Context.getConnectionManager().getActiveDevice(uniqueId); + if(activeDevice == null) { + throw new RuntimeException("The device has not yet registered to the server"); + } + + activeDevice.sendCommand(command); + + sendResponse(resp.getWriter(), JsonConverter.objectToJson(new Object())); + } + + private void sendRawCommand(HttpServletRequest req, HttpServletResponse resp) throws Exception { + JsonObject json = Json.createReader(req.getReader()).readObject(); + String uniqueId = json.getString("uniqueId"); + + ActiveDevice activeDevice = Context.getConnectionManager().getActiveDevice(uniqueId); + if(activeDevice == null) { + throw new RuntimeException("The device has not yet registered to the server"); + } + + String command = json.getString("command"); + activeDevice.write(command); + + sendResponse(resp.getWriter(), JsonConverter.objectToJson(new Object())); + } +} diff --git a/src/org/traccar/web/DeviceServlet.java b/src/org/traccar/web/DeviceServlet.java new file mode 100644 index 000000000..6b65efa8b --- /dev/null +++ b/src/org/traccar/web/DeviceServlet.java @@ -0,0 +1,75 @@ +/* + * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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.web; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.traccar.Context; +import org.traccar.model.Device; + +public class DeviceServlet extends BaseServlet { + + @Override + protected boolean handle(String command, HttpServletRequest req, HttpServletResponse resp) throws Exception { + + switch (command) { + case "/get": + get(req, resp); + break; + case "/add": + add(req, resp); + break; + case "/update": + update(req, resp); + break; + case "/remove": + remove(req, resp); + break; + default: + return false; + } + return true; + } + + private void get(HttpServletRequest req, HttpServletResponse resp) throws Exception { + sendResponse(resp.getWriter(), JsonConverter.arrayToJson( + Context.getDataManager().getDevices(getUserId(req)))); + } + + private void add(HttpServletRequest req, HttpServletResponse resp) throws Exception { + Device device = JsonConverter.objectFromJson(req.getReader(), new Device()); + Context.getDataManager().addDevice(device); + Context.getDataManager().linkDevice(getUserId(req), device.getId()); + Context.getPermissionsManager().refresh(); + sendResponse(resp.getWriter(), JsonConverter.objectToJson(device)); + } + + private void update(HttpServletRequest req, HttpServletResponse resp) throws Exception { + Device device = JsonConverter.objectFromJson(req.getReader(), new Device()); + Context.getPermissionsManager().checkDevice(getUserId(req), device.getId()); + Context.getDataManager().updateDevice(device); + sendResponse(resp.getWriter(), true); + } + + private void remove(HttpServletRequest req, HttpServletResponse resp) throws Exception { + Device device = JsonConverter.objectFromJson(req.getReader(), new Device()); + Context.getPermissionsManager().checkDevice(getUserId(req), device.getId()); + Context.getDataManager().removeDevice(device); + Context.getPermissionsManager().refresh(); + sendResponse(resp.getWriter(), true); + } + +} diff --git a/src/org/traccar/web/EnumFactory.java b/src/org/traccar/web/EnumFactory.java new file mode 100644 index 000000000..efbfd4b20 --- /dev/null +++ b/src/org/traccar/web/EnumFactory.java @@ -0,0 +1,20 @@ +package org.traccar.web; + +import org.traccar.model.Factory; + +import javax.json.JsonObject; + +public class EnumFactory & Factory> { + private Class commandTypeClass; + private String jsonKey; + + public EnumFactory(Class commandTypeClass, String type) { + this.commandTypeClass = commandTypeClass; + jsonKey = type; + } + + public K create(JsonObject json) { + Factory factory = Enum.valueOf(commandTypeClass, json.getString(jsonKey)); + return (K) factory.create(); + } +} diff --git a/src/org/traccar/web/JsonConverter.java b/src/org/traccar/web/JsonConverter.java new file mode 100644 index 000000000..7962ac8ab --- /dev/null +++ b/src/org/traccar/web/JsonConverter.java @@ -0,0 +1,155 @@ +/* + * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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.web; + +import java.beans.Introspector; +import java.io.Reader; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Collection; +import java.util.Date; +import java.util.Map; +import javax.json.Json; +import javax.json.JsonArray; +import javax.json.JsonArrayBuilder; +import javax.json.JsonObject; +import javax.json.JsonObjectBuilder; +import javax.json.JsonValue; +import org.traccar.model.Factory; +import org.traccar.model.MiscFormatter; + +public class JsonConverter { + + private static final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); + + public static Date parseDate(String value) throws ParseException { + return dateFormat.parse(value); + } + + public static T objectFromJson(Reader reader, T prototype) throws ParseException { + return objectFromJson(Json.createReader(reader).readObject(), prototype); + } + + public static T enumObjectFromJson(Reader reader, EnumFactory> factory) throws ParseException { + JsonObject json = Json.createReader(reader).readObject(); + T object = factory.create(json); + populateObject(json, object); + return object; + } + + public static T objectFromJson(JsonObject json, T prototype) throws ParseException { + T object = (T) prototype.create(); + populateObject(json, object); + return object; + } + + private static void populateObject(JsonObject json, Object object) throws ParseException { + Method[] methods = object.getClass().getMethods(); + + for (final Method method : methods) { + if (method.getName().startsWith("set") && method.getParameterTypes().length == 1) { + + final String name = Introspector.decapitalize(method.getName().substring(3)); + Class parameterType = method.getParameterTypes()[0]; + + if (json.containsKey(name)) try { + if (parameterType.equals(boolean.class)) { + method.invoke(object, json.getBoolean(name)); + } else if (parameterType.equals(int.class)) { + method.invoke(object, json.getJsonNumber(name).intValue()); + } else if (parameterType.equals(long.class)) { + if (json.get(name).getValueType() == JsonValue.ValueType.NUMBER) { + method.invoke(object, json.getJsonNumber(name).longValue()); + } + } else if (parameterType.equals(double.class)) { + method.invoke(object, json.getJsonNumber(name).doubleValue()); + } else if (parameterType.equals(String.class)) { + method.invoke(object, json.getString(name)); + } else if (parameterType.equals(Date.class)) { + method.invoke(object, dateFormat.parse(json.getString(name))); + } else if (parameterType.isEnum()) { + method.invoke(object, Enum.valueOf((Class) parameterType, json.getString(name))); + } else if (parameterType.equals(Map.class)) { + //method.invoke(object, json.getString(name)); + } else { + Object nestedObject = parameterType.newInstance(); + populateObject(json.getJsonObject(name), nestedObject); + method.invoke(object, nestedObject); + } + } catch (IllegalAccessException | InvocationTargetException | InstantiationException error) { + } + } + } + + } + + public static JsonObject objectToJson(T object) { + + JsonObjectBuilder json = Json.createObjectBuilder(); + + Method[] methods = object.getClass().getMethods(); + + for (Method method : methods) { + if(method.isAnnotationPresent(JsonIgnore.class)) { + continue; + } + if (method.getName().startsWith("get") && method.getParameterTypes().length == 0) { + String name = Introspector.decapitalize(method.getName().substring(3)); + try { + if (method.getReturnType().equals(boolean.class)) { + json.add(name, (Boolean) method.invoke(object)); + } else if (method.getReturnType().equals(int.class)) { + json.add(name, (Integer) method.invoke(object)); + } else if (method.getReturnType().equals(long.class)) { + json.add(name, (Long) method.invoke(object)); + } else if (method.getReturnType().equals(double.class)) { + json.add(name, (Double) method.invoke(object)); + } else if (method.getReturnType().equals(String.class)) { + String value = (String) method.invoke(object); + if (value != null) { + json.add(name, value); + } + } else if (method.getReturnType().equals(Date.class)) { + Date value = (Date) method.invoke(object); + if (value != null) { + json.add(name, dateFormat.format(value)); + } + } else if (method.getReturnType().equals(Map.class)) { + json.add(name, MiscFormatter.toJson((Map) method.invoke(object))); + } + } catch (IllegalAccessException | InvocationTargetException error) { + } + } + } + + return json.build(); + } + + public static JsonArray arrayToJson(Collection array) { + + JsonArrayBuilder json = Json.createArrayBuilder(); + + for (Object object : array) { + json.add(objectToJson(object)); + } + + return json.build(); + } + +} diff --git a/src/org/traccar/web/JsonIgnore.java b/src/org/traccar/web/JsonIgnore.java new file mode 100644 index 000000000..208cdc627 --- /dev/null +++ b/src/org/traccar/web/JsonIgnore.java @@ -0,0 +1,12 @@ +package org.traccar.web; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; + +@Retention(RetentionPolicy.RUNTIME) +@Target(value = {METHOD}) +public @interface JsonIgnore { +} diff --git a/src/org/traccar/web/MainServlet.java b/src/org/traccar/web/MainServlet.java new file mode 100644 index 000000000..eaadef245 --- /dev/null +++ b/src/org/traccar/web/MainServlet.java @@ -0,0 +1,80 @@ +/* + * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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.web; + +import java.io.IOException; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.traccar.Context; +import org.traccar.model.User; + +public class MainServlet extends BaseServlet { + + @Override + protected boolean handle(String command, HttpServletRequest req, HttpServletResponse resp) throws Exception { + + switch (command) { + case "/session": + session(req, resp); + break; + case "/login": + login(req, resp); + break; + case "/logout": + logout(req, resp); + break; + case "/register": + register(req, resp); + break; + default: + return false; + } + return true; + } + + private void session(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + User user = (User) req.getSession().getAttribute(USER_KEY); + if (user != null) { + sendResponse(resp.getWriter(), JsonConverter.objectToJson(user)); + } else { + sendResponse(resp.getWriter(), false); + } + } + + private void login(HttpServletRequest req, HttpServletResponse resp) throws Exception { + User user = Context.getDataManager().login( + req.getParameter("email"), req.getParameter("password")); + if (user != null) { + req.getSession().setAttribute(USER_KEY, user); + sendResponse(resp.getWriter(), JsonConverter.objectToJson(user)); + } else { + sendResponse(resp.getWriter(), false); + } + } + + private void logout(HttpServletRequest req, HttpServletResponse resp) throws Exception { + req.getSession().removeAttribute(USER_KEY); + sendResponse(resp.getWriter(), true); + } + + private void register(HttpServletRequest req, HttpServletResponse resp) throws Exception { + User user = JsonConverter.objectFromJson(req.getReader(), new User()); + Context.getDataManager().addUser(user); + sendResponse(resp.getWriter(), true); + } + +} diff --git a/src/org/traccar/web/PositionServlet.java b/src/org/traccar/web/PositionServlet.java new file mode 100644 index 000000000..a29b5b274 --- /dev/null +++ b/src/org/traccar/web/PositionServlet.java @@ -0,0 +1,47 @@ +/* + * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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.web; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.traccar.Context; + +public class PositionServlet extends BaseServlet { + + @Override + protected boolean handle(String command, HttpServletRequest req, HttpServletResponse resp) throws Exception { + + switch (command) { + case "/get": + get(req, resp); + break; + default: + return false; + } + return true; + } + + private void get(HttpServletRequest req, HttpServletResponse resp) throws Exception { + long deviceId = Long.valueOf(req.getParameter("deviceId")); + Context.getPermissionsManager().checkDevice(getUserId(req), deviceId); + sendResponse(resp.getWriter(), JsonConverter.arrayToJson( + Context.getDataManager().getPositions( + getUserId(req), deviceId, + JsonConverter.parseDate(req.getParameter("from")), + JsonConverter.parseDate(req.getParameter("to"))))); + } + +} diff --git a/src/org/traccar/web/ServerServlet.java b/src/org/traccar/web/ServerServlet.java new file mode 100644 index 000000000..631737eb0 --- /dev/null +++ b/src/org/traccar/web/ServerServlet.java @@ -0,0 +1,53 @@ +/* + * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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.web; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.traccar.Context; +import org.traccar.model.Server; + +public class ServerServlet extends BaseServlet { + + @Override + protected boolean handle(String command, HttpServletRequest req, HttpServletResponse resp) throws Exception { + + switch (command) { + case "/get": + get(req, resp); + break; + case "/update": + update(req, resp); + break; + default: + return false; + } + return true; + } + + private void get(HttpServletRequest req, HttpServletResponse resp) throws Exception { + sendResponse(resp.getWriter(), JsonConverter.objectToJson( + Context.getDataManager().getServer())); + } + + private void update(HttpServletRequest req, HttpServletResponse resp) throws Exception { + Server server = JsonConverter.objectFromJson(req.getReader(), new Server()); + Context.getPermissionsManager().checkAdmin(getUserId(req)); + Context.getDataManager().updateServer(server); + sendResponse(resp.getWriter(), true); + } + +} diff --git a/src/org/traccar/web/UserServlet.java b/src/org/traccar/web/UserServlet.java new file mode 100644 index 000000000..eac4b0b13 --- /dev/null +++ b/src/org/traccar/web/UserServlet.java @@ -0,0 +1,81 @@ +/* + * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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.web; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.traccar.Context; +import org.traccar.model.User; + +public class UserServlet extends BaseServlet { + + @Override + protected boolean handle(String command, HttpServletRequest req, HttpServletResponse resp) throws Exception { + + switch (command) { + case "/get": + get(req, resp); + break; + case "/add": + add(req, resp); + break; + case "/update": + update(req, resp); + break; + case "/remove": + remove(req, resp); + break; + default: + return false; + } + return true; + } + + private void get(HttpServletRequest req, HttpServletResponse resp) throws Exception { + Context.getPermissionsManager().checkAdmin(getUserId(req)); + sendResponse(resp.getWriter(), JsonConverter.arrayToJson( + Context.getDataManager().getUsers())); + } + + private void add(HttpServletRequest req, HttpServletResponse resp) throws Exception { + User user = JsonConverter.objectFromJson(req.getReader(), new User()); + Context.getPermissionsManager().checkUser(getUserId(req), user.getId()); + Context.getDataManager().addUser(user); + Context.getPermissionsManager().refresh(); + sendResponse(resp.getWriter(), JsonConverter.objectToJson(user)); + } + + private void update(HttpServletRequest req, HttpServletResponse resp) throws Exception { + User user = JsonConverter.objectFromJson(req.getReader(), new User()); + if (user.getAdmin()) { + Context.getPermissionsManager().checkAdmin(getUserId(req)); + } else { + Context.getPermissionsManager().checkUser(getUserId(req), user.getId()); + } + Context.getDataManager().updateUser(user); + Context.getPermissionsManager().refresh(); + sendResponse(resp.getWriter(), true); + } + + private void remove(HttpServletRequest req, HttpServletResponse resp) throws Exception { + User user = JsonConverter.objectFromJson(req.getReader(), new User()); + Context.getPermissionsManager().checkUser(getUserId(req), user.getId()); + Context.getDataManager().removeUser(user); + Context.getPermissionsManager().refresh(); + sendResponse(resp.getWriter(), true); + } + +} diff --git a/src/org/traccar/web/WebServer.java b/src/org/traccar/web/WebServer.java new file mode 100644 index 000000000..698796467 --- /dev/null +++ b/src/org/traccar/web/WebServer.java @@ -0,0 +1,116 @@ +/* + * Copyright 2012 - 2015 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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.web; + +import java.net.InetSocketAddress; +import javax.naming.InitialContext; +import javax.sql.DataSource; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.HandlerList; +import org.eclipse.jetty.server.handler.ResourceHandler; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.webapp.WebAppContext; +import org.traccar.Config; +import org.traccar.helper.Log; + +/** + * Integrated HTTP server + */ +public class WebServer { + + private Server server; + + private void initServer(Config config) { + + String address = config.getString("web.address"); + int port = config.getInteger("web.port", 8082); + if (address == null) { + server = new Server(port); + } else { + server = new Server(new InetSocketAddress(address, port)); + } + } + + public WebServer(Config config, DataSource dataSource) { + + initServer(config); + + try { + javax.naming.Context context = new InitialContext(); + context.bind("java:/DefaultDS", dataSource); + } catch (Exception error) { + Log.warning(error); + } + + WebAppContext webapp = new WebAppContext(); + webapp.setContextPath("/"); + webapp.setWar(config.getString("web.application")); + server.setHandler(webapp); + } + + public WebServer(Config config) { + + initServer(config); + + ServletContextHandler servletHandler = new ServletContextHandler(ServletContextHandler.SESSIONS); + servletHandler.setContextPath("/api"); + servletHandler.addServlet(new ServletHolder(new AsyncServlet()), "/async/*"); + servletHandler.addServlet(new ServletHolder(new ServerServlet()), "/server/*"); + servletHandler.addServlet(new ServletHolder(new UserServlet()), "/user/*"); + servletHandler.addServlet(new ServletHolder(new DeviceServlet()), "/device/*"); + servletHandler.addServlet(new ServletHolder(new PositionServlet()), "/position/*"); + servletHandler.addServlet(new ServletHolder(new CommandsServlet()), "/commands/*"); + servletHandler.addServlet(new ServletHolder(new MainServlet()), "/*"); + + /*ResourceHandler mobileResourceHandler = new ResourceHandler(); + mobileResourceHandler.setResourceBase(properties.getProperty("web.mobile")); + mobileResourceHandler.setWelcomeFiles(new String[] {"index.html"}); + ContextHandler mobileContext = new ContextHandler("/m"); + mobileContext.setHandler(mobileResourceHandler);*/ + + ResourceHandler resourceHandler = new ResourceHandler(); + resourceHandler.setResourceBase(config.getString("web.path")); + if (config.getBoolean("web.debug")) { + resourceHandler.setWelcomeFiles(new String[] { "debug.html" }); + } else { + resourceHandler.setWelcomeFiles(new String[] { "release.html" }); + } + + HandlerList handlerList = new HandlerList(); + handlerList.setHandlers(new Handler[] {servletHandler, resourceHandler}); + + server.setHandler(handlerList); + } + + public void start() { + try { + server.start(); + } catch (Exception error) { + Log.warning(error); + } + } + + public void stop() { + try { + server.stop(); + } catch (Exception error) { + Log.warning(error); + } + } + +} diff --git a/test/org/traccar/http/JsonConverterTest.java b/test/org/traccar/http/JsonConverterTest.java deleted file mode 100644 index b318420e0..000000000 --- a/test/org/traccar/http/JsonConverterTest.java +++ /dev/null @@ -1,171 +0,0 @@ -package org.traccar.http; - -import org.junit.Test; -import org.traccar.http.JsonConverter; -import org.traccar.model.Factory; - -import java.io.Reader; -import java.io.StringReader; -import java.text.ParseException; -import java.util.Calendar; -import java.util.Date; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -public class JsonConverterTest { - - private T convert(String jsonString, T prototype) throws ParseException { - Reader r = new StringReader( - jsonString); - - return JsonConverter.objectFromJson(r, prototype); - } - - @Test - public void primitiveConversion() throws ParseException { - - AllPrimitives o = convert("{" + - "\"aBoolean\": true, " + - "\"anInt\": 42, " + - "\"aDouble\": 41.99, " + - "\"aString\": \"discworld\", " + - "\"aDate\":\"2015-07-09T19:02:17\"" + - "}", - new AllPrimitives()); - - assertEquals(true, o.getaBoolean()); - assertEquals(42, o.getAnInt()); - assertEquals(41.99, o.getaDouble(), 0.001); - assertEquals("discworld", o.getaString()); - - Calendar c = Calendar.getInstance(); - c.setTime(o.getaDate()); - assertEquals(2015, c.get(Calendar.YEAR)); - assertEquals(Calendar.JULY, c.get(Calendar.MONTH)); - assertEquals(9, c.get(Calendar.DAY_OF_MONTH)); - assertEquals(19, c.get(Calendar.HOUR_OF_DAY)); - assertEquals(2, c.get(Calendar.MINUTE)); - assertEquals(17, c.get(Calendar.SECOND)); - } - - public static class AllPrimitives implements Factory { - - private boolean aBoolean; - private int anInt; - private double aDouble; - private String aString; - private Date aDate; - - - @Override - public Object create() { - return new AllPrimitives(); - } - - public boolean getaBoolean() { - return aBoolean; - } - - public void setaBoolean(boolean aBoolean) { - this.aBoolean = aBoolean; - } - - public int getAnInt() { - return anInt; - } - - public void setAnInt(int anInt) { - this.anInt = anInt; - } - - public double getaDouble() { - return aDouble; - } - - public void setaDouble(double aDouble) { - this.aDouble = aDouble; - } - - public String getaString() { - return aString; - } - - public void setaString(String aString) { - this.aString = aString; - } - - public Date getaDate() { - return aDate; - } - - public void setaDate(Date aDate) { - this.aDate = aDate; - } - } - - - @Test - public void enumConversion() throws ParseException { - ObjectWithEnum o = convert("{\"anEnum\": \"VALUE2\"}", new ObjectWithEnum()); - assertEquals(TestEnum.VALUE2, o.getAnEnum()); - } - - - public enum TestEnum { - VALUE1, VALUE2 - } - - public static class ObjectWithEnum implements Factory { - private TestEnum anEnum; - - public TestEnum getAnEnum() { - return anEnum; - } - - public void setAnEnum(TestEnum anEnum) { - this.anEnum = anEnum; - } - - @Override - public Object create() { - return new ObjectWithEnum(); - } - } - - - @Test - public void nestedObjectsConversion() throws ParseException { - NestedObjects o = convert("{\"name\": \"Rincewind\", \"nestedObject\": {\"anEnum\":\"VALUE1\"}}", new NestedObjects()); - assertEquals("Rincewind", o.getName()); - assertNotNull("The nested object should be populated", o.getNestedObject()); - assertEquals(TestEnum.VALUE1, o.getNestedObject().getAnEnum()); - } - - public static class NestedObjects implements Factory { - - private String name; - private ObjectWithEnum nestedObject; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public ObjectWithEnum getNestedObject() { - return nestedObject; - } - - public void setNestedObject(ObjectWithEnum nestedObject) { - this.nestedObject = nestedObject; - } - - @Override - public Object create() { - return new NestedObjects(); - } - } -} diff --git a/test/org/traccar/web/JsonConverterTest.java b/test/org/traccar/web/JsonConverterTest.java new file mode 100644 index 000000000..07d70d3f2 --- /dev/null +++ b/test/org/traccar/web/JsonConverterTest.java @@ -0,0 +1,170 @@ +package org.traccar.web; + +import org.junit.Test; +import org.traccar.model.Factory; + +import java.io.Reader; +import java.io.StringReader; +import java.text.ParseException; +import java.util.Calendar; +import java.util.Date; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class JsonConverterTest { + + private T convert(String jsonString, T prototype) throws ParseException { + Reader r = new StringReader( + jsonString); + + return JsonConverter.objectFromJson(r, prototype); + } + + @Test + public void primitiveConversion() throws ParseException { + + AllPrimitives o = convert("{" + + "\"aBoolean\": true, " + + "\"anInt\": 42, " + + "\"aDouble\": 41.99, " + + "\"aString\": \"discworld\", " + + "\"aDate\":\"2015-07-09T19:02:17\"" + + "}", + new AllPrimitives()); + + assertEquals(true, o.getaBoolean()); + assertEquals(42, o.getAnInt()); + assertEquals(41.99, o.getaDouble(), 0.001); + assertEquals("discworld", o.getaString()); + + Calendar c = Calendar.getInstance(); + c.setTime(o.getaDate()); + assertEquals(2015, c.get(Calendar.YEAR)); + assertEquals(Calendar.JULY, c.get(Calendar.MONTH)); + assertEquals(9, c.get(Calendar.DAY_OF_MONTH)); + assertEquals(19, c.get(Calendar.HOUR_OF_DAY)); + assertEquals(2, c.get(Calendar.MINUTE)); + assertEquals(17, c.get(Calendar.SECOND)); + } + + public static class AllPrimitives implements Factory { + + private boolean aBoolean; + private int anInt; + private double aDouble; + private String aString; + private Date aDate; + + + @Override + public Object create() { + return new AllPrimitives(); + } + + public boolean getaBoolean() { + return aBoolean; + } + + public void setaBoolean(boolean aBoolean) { + this.aBoolean = aBoolean; + } + + public int getAnInt() { + return anInt; + } + + public void setAnInt(int anInt) { + this.anInt = anInt; + } + + public double getaDouble() { + return aDouble; + } + + public void setaDouble(double aDouble) { + this.aDouble = aDouble; + } + + public String getaString() { + return aString; + } + + public void setaString(String aString) { + this.aString = aString; + } + + public Date getaDate() { + return aDate; + } + + public void setaDate(Date aDate) { + this.aDate = aDate; + } + } + + + @Test + public void enumConversion() throws ParseException { + ObjectWithEnum o = convert("{\"anEnum\": \"VALUE2\"}", new ObjectWithEnum()); + assertEquals(TestEnum.VALUE2, o.getAnEnum()); + } + + + public enum TestEnum { + VALUE1, VALUE2 + } + + public static class ObjectWithEnum implements Factory { + private TestEnum anEnum; + + public TestEnum getAnEnum() { + return anEnum; + } + + public void setAnEnum(TestEnum anEnum) { + this.anEnum = anEnum; + } + + @Override + public Object create() { + return new ObjectWithEnum(); + } + } + + + @Test + public void nestedObjectsConversion() throws ParseException { + NestedObjects o = convert("{\"name\": \"Rincewind\", \"nestedObject\": {\"anEnum\":\"VALUE1\"}}", new NestedObjects()); + assertEquals("Rincewind", o.getName()); + assertNotNull("The nested object should be populated", o.getNestedObject()); + assertEquals(TestEnum.VALUE1, o.getNestedObject().getAnEnum()); + } + + public static class NestedObjects implements Factory { + + private String name; + private ObjectWithEnum nestedObject; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public ObjectWithEnum getNestedObject() { + return nestedObject; + } + + public void setNestedObject(ObjectWithEnum nestedObject) { + this.nestedObject = nestedObject; + } + + @Override + public Object create() { + return new NestedObjects(); + } + } +} -- cgit v1.2.3