diff options
author | Anton Tananaev <anton.tananaev@gmail.com> | 2013-01-09 20:32:40 +1300 |
---|---|---|
committer | Anton Tananaev <anton.tananaev@gmail.com> | 2013-01-09 20:32:40 +1300 |
commit | f0b99fd68cb3b6b484d50c1f9ebc0e77997fd2dd (patch) | |
tree | cd9beb89c07dbeacbb26d9df8a1a42502d206c74 /src/org | |
parent | a7c6a5f03c0ac94603b5238c59a275a239d16b73 (diff) | |
download | trackermap-web-f0b99fd68cb3b6b484d50c1f9ebc0e77997fd2dd.tar.gz trackermap-web-f0b99fd68cb3b6b484d50c1f9ebc0e77997fd2dd.tar.bz2 trackermap-web-f0b99fd68cb3b6b484d50c1f9ebc0e77997fd2dd.zip |
Completely rewritten project
Diffstat (limited to 'src/org')
28 files changed, 1719 insertions, 0 deletions
diff --git a/src/org/traccar/web/Traccar.gwt.xml b/src/org/traccar/web/Traccar.gwt.xml new file mode 100644 index 00000000..00813723 --- /dev/null +++ b/src/org/traccar/web/Traccar.gwt.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 2.5.0//EN" + "http://google-web-toolkit.googlecode.com/svn/tags/2.5.0/distro-source/core/src/gwt-module.dtd"> + +<module rename-to='traccar'> + + <inherits name='com.google.gwt.user.User'/> + <inherits name='com.sencha.gxt.ui.GXT'/> + <inherits name='org.gwtopenmaps.openlayers.OpenLayers' /> + + <entry-point class='org.traccar.web.client.Traccar'/> + + <source path='client'/> + <source path='shared'/> + +</module> diff --git a/src/org/traccar/web/client/Application.java b/src/org/traccar/web/client/Application.java new file mode 100644 index 00000000..9b14d421 --- /dev/null +++ b/src/org/traccar/web/client/Application.java @@ -0,0 +1,98 @@ +package org.traccar.web.client; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.traccar.web.client.controller.ArchiveController; +import org.traccar.web.client.controller.DeviceController; +import org.traccar.web.client.controller.MapController; +import org.traccar.web.client.model.DataService; +import org.traccar.web.client.model.DataServiceAsync; +import org.traccar.web.client.view.ApplicationView; +import org.traccar.web.shared.model.Device; +import org.traccar.web.shared.model.Position; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.user.client.ui.RootPanel; + +public class Application { + + private static final DataServiceAsync dataService = GWT.create(DataService.class); + + public static DataServiceAsync getDataService() { + return dataService; + } + + private final DeviceController deviceController; + private final MapController mapController; + private final ArchiveController archiveController; + + private ApplicationView view; + + public Application() { + deviceController = new DeviceController(deviceHandler); + mapController = new MapController(); + archiveController = new ArchiveController(archiveHanlder); + + view = new ApplicationView( + deviceController.getView(), mapController.getView(), archiveController.getView()); + } + + public void run() { + RootPanel.get().add(view); + + deviceController.run(); + mapController.run(); + archiveController.run(); + } + + private Map<Long, Device> devices = new HashMap<Long, Device>(); + + private DeviceController.DeviceHandler deviceHandler = new DeviceController.DeviceHandler() { + + @Override + public void onLoad(List<Device> devices) { + Application.this.devices.clear(); + for (Device device : devices) { + Application.this.devices.put(device.getId(), device); + } + } + + @Override + public void onSelected(Device device) { + mapController.select(device); + } + + @Override + public void onAdd(Device device) { + devices.put(device.getId(), device); + archiveController.updateDevices(devices.values()); + mapController.update(); + } + + @Override + public void onUpdate(Device device) { + devices.put(device.getId(), device); + archiveController.updateDevices(devices.values()); + } + + @Override + public void onRemove(Device device) { + devices.remove(device.getId()); + archiveController.updateDevices(devices.values()); + mapController.update(); + } + + }; + + private ArchiveController.ArchiveHandler archiveHanlder = new ArchiveController.ArchiveHandler() { + + @Override + public void onSelected(Position position) { + // TODO select something on map? + } + + }; + +} diff --git a/src/org/traccar/web/client/Traccar.java b/src/org/traccar/web/client/Traccar.java new file mode 100644 index 00000000..5d56d402 --- /dev/null +++ b/src/org/traccar/web/client/Traccar.java @@ -0,0 +1,19 @@ +package org.traccar.web.client; + +import org.traccar.web.client.controller.LoginController; + +import com.google.gwt.core.client.EntryPoint; + +public class Traccar implements EntryPoint, LoginController.LoginHandler { + + @Override + public void onModuleLoad() { + new LoginController().login(this); + } + + @Override + public void onLogin() { + new Application().run(); + } + +} diff --git a/src/org/traccar/web/client/controller/ArchiveController.java b/src/org/traccar/web/client/controller/ArchiveController.java new file mode 100644 index 00000000..598a744d --- /dev/null +++ b/src/org/traccar/web/client/controller/ArchiveController.java @@ -0,0 +1,44 @@ +package org.traccar.web.client.controller; + +import java.util.Collection; + +import org.traccar.web.client.view.ArchiveView; +import org.traccar.web.shared.model.Device; +import org.traccar.web.shared.model.Position; + +import com.sencha.gxt.widget.core.client.ContentPanel; + +public class ArchiveController implements ContentController, ArchiveView.ArchiveHandler { + + public interface ArchiveHandler { + public void onSelected(Position position); + } + + private ArchiveHandler archiveHandler; + + private ArchiveView archiveView; + + public ArchiveController(ArchiveHandler archiveHandler) { + this.archiveHandler = archiveHandler; + archiveView = new ArchiveView(this); + } + + @Override + public ContentPanel getView() { + return archiveView.getView(); + } + + @Override + public void run() { + } + + public void updateDevices(Collection<Device> devices) { + + } + + @Override + public void onSelected(Position position) { + archiveHandler.onSelected(position); + } + +} diff --git a/src/org/traccar/web/client/controller/ContentController.java b/src/org/traccar/web/client/controller/ContentController.java new file mode 100644 index 00000000..7e8d6778 --- /dev/null +++ b/src/org/traccar/web/client/controller/ContentController.java @@ -0,0 +1,11 @@ +package org.traccar.web.client.controller; + +import com.sencha.gxt.widget.core.client.ContentPanel; + +public interface ContentController { + + public ContentPanel getView(); + + public void run(); + +} diff --git a/src/org/traccar/web/client/controller/DeviceController.java b/src/org/traccar/web/client/controller/DeviceController.java new file mode 100644 index 00000000..b24c205e --- /dev/null +++ b/src/org/traccar/web/client/controller/DeviceController.java @@ -0,0 +1,109 @@ +package org.traccar.web.client.controller; + +import java.util.List; + +import org.traccar.web.client.Application; +import org.traccar.web.client.model.BaseAsyncCallback; +import org.traccar.web.client.view.DeviceDialog; +import org.traccar.web.client.view.DeviceView; +import org.traccar.web.shared.model.Device; + +import com.sencha.gxt.widget.core.client.ContentPanel; +import com.sencha.gxt.widget.core.client.Dialog.PredefinedButton; +import com.sencha.gxt.widget.core.client.box.ConfirmMessageBox; +import com.sencha.gxt.widget.core.client.event.HideEvent; +import com.sencha.gxt.widget.core.client.event.HideEvent.HideHandler; + +public class DeviceController implements ContentController, DeviceView.DeviceHandler { + + public interface DeviceHandler { + public void onLoad(List<Device> devices); + public void onSelected(Device device); + public void onAdd(Device device); + public void onUpdate(Device device); + public void onRemove(Device device); + } + + private DeviceHandler deviceHandler; + + private DeviceView deviceView; + + public DeviceController(DeviceHandler deviceHandler) { + this.deviceHandler = deviceHandler; + deviceView = new DeviceView(this); + } + + @Override + public ContentPanel getView() { + return deviceView.getView(); + } + + @Override + public void run() { + Application.getDataService().getDevices(new BaseAsyncCallback<List<Device>>() { + @Override + public void onSuccess(List<Device> result) { + deviceView.load(result); + deviceHandler.onLoad(result); + } + }); + } + + @Override + public void onSelected(Device device) { + deviceHandler.onSelected(device); + } + + @Override + public void onAdd() { + new DeviceDialog(new Device(), new DeviceDialog.DeviceHandler() { + @Override + public void onSave(Device device) { + Application.getDataService().addDevice(device, new BaseAsyncCallback<Device>() { + @Override + public void onSuccess(Device result) { + deviceView.add(result); + deviceHandler.onAdd(result); + } + }); + } + }).show(); + } + + @Override + public void onEdit(Device device) { + new DeviceDialog(new Device(device), new DeviceDialog.DeviceHandler() { + @Override + public void onSave(Device device) { + Application.getDataService().updateDevice(device, new BaseAsyncCallback<Device>() { + @Override + public void onSuccess(Device result) { + deviceView.update(result); + deviceHandler.onUpdate(result); + } + }); + } + }).show(); + } + + @Override + public void onRemove(final Device device) { + final ConfirmMessageBox dialog = new ConfirmMessageBox("Confirm", "Are you sure you want remove device?"); + dialog.addHideHandler(new HideHandler() { + @Override + public void onHide(HideEvent event) { + if (dialog.getHideButton() == dialog.getButtonById(PredefinedButton.YES.name())) { + Application.getDataService().removeDevice(device, new BaseAsyncCallback<Device>() { + @Override + public void onSuccess(Device result) { + deviceView.remove(device); + deviceHandler.onRemove(device); + } + }); + } + } + }); + dialog.show(); + } + +} diff --git a/src/org/traccar/web/client/controller/LoginController.java b/src/org/traccar/web/client/controller/LoginController.java new file mode 100644 index 00000000..ceda85cf --- /dev/null +++ b/src/org/traccar/web/client/controller/LoginController.java @@ -0,0 +1,81 @@ +package org.traccar.web.client.controller; + +import org.traccar.web.client.Application; +import org.traccar.web.client.model.BaseAsyncCallback; +import org.traccar.web.client.view.LoginDialog; + +import com.sencha.gxt.widget.core.client.box.AlertMessageBox; + +public class LoginController implements LoginDialog.LoginHandler { + + private LoginDialog dialog; + + public interface LoginHandler { + public void onLogin(); + } + + private LoginHandler loginHandler; + + public void login(final LoginHandler loginHandler) { + this.loginHandler = loginHandler; + + Application.getDataService().authenticated(new BaseAsyncCallback<Boolean>() { + @Override + public void onSuccess(Boolean result) { + if (result) { + loginHandler.onLogin(); + } else { + dialog = new LoginDialog(LoginController.this); + dialog.show(); + } + } + }); + } + + private boolean validate(String login, String password) { + if (login == null || login.isEmpty() || password == null || password.isEmpty()) { + new AlertMessageBox("Error", "User name and password must not be empty").show(); + return false; + } + return true; + } + + @Override + public void onLogin(String login, String password) { + if (validate(login, password)) { + Application.getDataService().authenticate(login, password, new BaseAsyncCallback<Boolean>() { + @Override + public void onSuccess(Boolean result) { + if (result) { + if (loginHandler != null) { + dialog.hide(); + loginHandler.onLogin(); + } + } else { + new AlertMessageBox("Error", "User name or password is invalid").show(); + } + } + }); + } + } + + @Override + public void onRegister(String login, String password) { + if (validate(login, password)) { + Application.getDataService().register(login, password, new BaseAsyncCallback<Boolean>() { + @Override + public void onSuccess(Boolean result) { + if (result) { + if (loginHandler != null) { + dialog.hide(); + loginHandler.onLogin(); + } + } else { + new AlertMessageBox("Error", "Registration error").show(); + } + } + }); + } + } + +} diff --git a/src/org/traccar/web/client/controller/MapController.java b/src/org/traccar/web/client/controller/MapController.java new file mode 100644 index 00000000..5a53cf22 --- /dev/null +++ b/src/org/traccar/web/client/controller/MapController.java @@ -0,0 +1,56 @@ +package org.traccar.web.client.controller; + +import org.traccar.web.client.view.MapView; +import org.traccar.web.shared.model.Device; + +import com.google.gwt.user.client.Timer; +import com.sencha.gxt.widget.core.client.ContentPanel; + +public class MapController implements ContentController { + + private static final int UPDATE_INTERVAL = 10000; + + private MapView mapView; + + public MapController() { + mapView = new MapView(); + } + + @Override + public ContentPanel getView() { + return mapView.getView(); + } + + private Timer updateTimer; + + @Override + public void run() { + updateTimer = new Timer() { + @Override + public void run() { + update(); + } + }; + update(); + } + + public void update() { + updateTimer.cancel(); + /*Application.getDataService().getLatestPositions(new AsyncCallback<List<Position>>() { + @Override + public void onSuccess(List<Position> result) { + mapView.showPositions(result); + updateTimer.schedule(UPDATE_INTERVAL); + } + @Override + public void onFailure(Throwable caught) { + updateTimer.schedule(UPDATE_INTERVAL); + } + });*/ + } + + public void select(Device device) { + mapView.select(device, true); + } + +} diff --git a/src/org/traccar/web/client/model/BaseAsyncCallback.java b/src/org/traccar/web/client/model/BaseAsyncCallback.java new file mode 100644 index 00000000..2cc8741b --- /dev/null +++ b/src/org/traccar/web/client/model/BaseAsyncCallback.java @@ -0,0 +1,17 @@ +package org.traccar.web.client.model; + +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.sencha.gxt.widget.core.client.box.AlertMessageBox; + +public class BaseAsyncCallback<T> implements AsyncCallback<T> { + + @Override + public void onFailure(Throwable caught) { + new AlertMessageBox("Error", "Remote procedure call error").show(); + } + + @Override + public void onSuccess(T result) { + } + +} diff --git a/src/org/traccar/web/client/model/DataService.java b/src/org/traccar/web/client/model/DataService.java new file mode 100644 index 00000000..d8d71520 --- /dev/null +++ b/src/org/traccar/web/client/model/DataService.java @@ -0,0 +1,33 @@ +package org.traccar.web.client.model; + +import java.util.Date; +import java.util.List; + +import org.traccar.web.shared.model.Device; +import org.traccar.web.shared.model.Position; + +import com.google.gwt.user.client.rpc.RemoteService; +import com.google.gwt.user.client.rpc.RemoteServiceRelativePath; + +@RemoteServiceRelativePath("dataService") +public interface DataService extends RemoteService { + + boolean authenticated(); + + boolean authenticate(String login, String password); + + boolean register(String login, String password); + + List<Device> getDevices(); + + Device addDevice(Device device); + + Device updateDevice(Device device); + + Device removeDevice(Device device); + + List<Position> getPositions(Device device, Date from, Date to); + + List<Position> getLatestPositions(); + +} diff --git a/src/org/traccar/web/client/model/DataServiceAsync.java b/src/org/traccar/web/client/model/DataServiceAsync.java new file mode 100644 index 00000000..357beb5d --- /dev/null +++ b/src/org/traccar/web/client/model/DataServiceAsync.java @@ -0,0 +1,31 @@ +package org.traccar.web.client.model; + +import java.util.Date; +import java.util.List; + +import org.traccar.web.shared.model.Device; +import org.traccar.web.shared.model.Position; + +import com.google.gwt.user.client.rpc.AsyncCallback; + +public interface DataServiceAsync { + + void authenticate(String login, String password, AsyncCallback<Boolean> callback); + + void authenticated(AsyncCallback<Boolean> callback); + + void register(String login, String password, AsyncCallback<Boolean> callback); + + void getDevices(AsyncCallback<List<Device>> callback); + + void addDevice(Device device, AsyncCallback<Device> callback); + + void updateDevice(Device device, AsyncCallback<Device> callback); + + void removeDevice(Device device, AsyncCallback<Device> callback); + + void getLatestPositions(AsyncCallback<List<Position>> callback); + + void getPositions(Device device, Date from, Date to, AsyncCallback<List<Position>> callback); + +} diff --git a/src/org/traccar/web/client/model/DeviceProperties.java b/src/org/traccar/web/client/model/DeviceProperties.java new file mode 100644 index 00000000..2c56ff6d --- /dev/null +++ b/src/org/traccar/web/client/model/DeviceProperties.java @@ -0,0 +1,17 @@ +package org.traccar.web.client.model; + +import org.traccar.web.shared.model.Device; + +import com.sencha.gxt.core.client.ValueProvider; +import com.sencha.gxt.data.shared.ModelKeyProvider; +import com.sencha.gxt.data.shared.PropertyAccess; + +public interface DeviceProperties extends PropertyAccess<Device> { + + ModelKeyProvider<Device> id(); + + ValueProvider<Device, String> uniqueId(); + + ValueProvider<Device, String> name(); + +} diff --git a/src/org/traccar/web/client/model/PositionProperties.java b/src/org/traccar/web/client/model/PositionProperties.java new file mode 100644 index 00000000..111614a3 --- /dev/null +++ b/src/org/traccar/web/client/model/PositionProperties.java @@ -0,0 +1,33 @@ +package org.traccar.web.client.model; + +import java.util.Date; + +import org.traccar.web.shared.model.Position; + +import com.sencha.gxt.core.client.ValueProvider; +import com.sencha.gxt.data.shared.ModelKeyProvider; +import com.sencha.gxt.data.shared.PropertyAccess; + +public interface PositionProperties extends PropertyAccess<Position> { + + ModelKeyProvider<Position> id(); + + ValueProvider<Position, Date> time(); + + ValueProvider<Position, Boolean> valid(); + + ValueProvider<Position, Double> latitude(); + + ValueProvider<Position, Double> longitude(); + + ValueProvider<Position, Double> altitude(); + + ValueProvider<Position, Double> speed(); + + ValueProvider<Position, Double> course(); + + ValueProvider<Position, Double> power(); + + ValueProvider<Position, String> address(); + +} diff --git a/src/org/traccar/web/client/view/ApplicationView.java b/src/org/traccar/web/client/view/ApplicationView.java new file mode 100644 index 00000000..bff8854f --- /dev/null +++ b/src/org/traccar/web/client/view/ApplicationView.java @@ -0,0 +1,33 @@ +package org.traccar.web.client.view; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.Widget; +import com.sencha.gxt.widget.core.client.ContentPanel; + +public class ApplicationView extends Composite { + + private static ApplicationViewUiBinder uiBinder = GWT.create(ApplicationViewUiBinder.class); + + interface ApplicationViewUiBinder extends UiBinder<Widget, ApplicationView> { + } + + @UiField(provided = true) + ContentPanel devicePanel; + + @UiField(provided = true) + ContentPanel mapPanel; + + @UiField(provided = true) + ContentPanel archivePanel; + + public ApplicationView(ContentPanel deviceView, ContentPanel mapView, ContentPanel archiveView) { + devicePanel = deviceView; + mapPanel = mapView; + archivePanel = archiveView; + initWidget(uiBinder.createAndBindUi(this)); + } + +} diff --git a/src/org/traccar/web/client/view/ApplicationView.ui.xml b/src/org/traccar/web/client/view/ApplicationView.ui.xml new file mode 100644 index 00000000..2faae1e7 --- /dev/null +++ b/src/org/traccar/web/client/view/ApplicationView.ui.xml @@ -0,0 +1,42 @@ +<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent"> +<ui:UiBinder + xmlns:ui="urn:ui:com.google.gwt.uibinder" + xmlns:g="urn:import:com.google.gwt.user.client.ui" + xmlns:container="urn:import:com.sencha.gxt.widget.core.client.container" + xmlns:gxt="urn:import:com.sencha.gxt.widget.core.client"> + + <ui:with type="com.sencha.gxt.core.client.util.Margins" field="westMargins"> + <ui:attributes top="5" right="5" bottom="0" left="5" /> + </ui:with> + <ui:with type="com.sencha.gxt.core.client.util.Margins" field="centerMargins"> + <ui:attributes top="5" right="5" bottom="0" left="0" /> + </ui:with> + <ui:with type="com.sencha.gxt.core.client.util.Margins" field="southMargins"> + <ui:attributes top="5" right="5" bottom="5" left="5" /> + </ui:with> + + <ui:with type="com.sencha.gxt.widget.core.client.container.BorderLayoutContainer.BorderLayoutData" field="westData"> + <ui:attributes size="0.15" maxSize="2147483647" margins="{westMargins}" split="true" /> + </ui:with> + <ui:with type="com.sencha.gxt.widget.core.client.container.MarginData" field="centerData"> + <ui:attributes margins="{centerMargins}" /> + </ui:with> + <ui:with type="com.sencha.gxt.widget.core.client.container.BorderLayoutContainer.BorderLayoutData" field="southData"> + <ui:attributes size="0.25" maxSize="2147483647" margins="{southMargins}" split="true" /> + </ui:with> + + <container:Viewport> + <container:BorderLayoutContainer> + <container:west layoutData="{westData}"> + <gxt:ContentPanel ui:field="devicePanel" /> + </container:west> + <container:center layoutData="{centerData}"> + <gxt:ContentPanel ui:field="mapPanel" /> + </container:center> + <container:south layoutData="{southData}"> + <gxt:ContentPanel ui:field="archivePanel" /> + </container:south> + </container:BorderLayoutContainer> + </container:Viewport> + +</ui:UiBinder> diff --git a/src/org/traccar/web/client/view/ArchiveView.java b/src/org/traccar/web/client/view/ArchiveView.java new file mode 100644 index 00000000..79621ba9 --- /dev/null +++ b/src/org/traccar/web/client/view/ArchiveView.java @@ -0,0 +1,79 @@ +package org.traccar.web.client.view; + +import java.util.Date; +import java.util.LinkedList; +import java.util.List; + +import org.traccar.web.client.model.PositionProperties; +import org.traccar.web.shared.model.Position; + +import com.google.gwt.cell.client.DateCell; +import com.google.gwt.core.client.GWT; +import com.google.gwt.i18n.client.DateTimeFormat; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.Widget; +import com.sencha.gxt.data.shared.ListStore; +import com.sencha.gxt.widget.core.client.ContentPanel; +import com.sencha.gxt.widget.core.client.grid.ColumnConfig; +import com.sencha.gxt.widget.core.client.grid.ColumnModel; +import com.sencha.gxt.widget.core.client.grid.Grid; + +public class ArchiveView extends Composite { + + private static ArchiveViewUiBinder uiBinder = GWT.create(ArchiveViewUiBinder.class); + + interface ArchiveViewUiBinder extends UiBinder<Widget, ArchiveView> { + } + + public interface ArchiveHandler { + public void onSelected(Position position); + } + + private ArchiveHandler archiveHandler; + + @UiField + ContentPanel contentPanel; + + public ContentPanel getView() { + return contentPanel; + } + + @UiField(provided = true) + ColumnModel<Position> columnModel; + + @UiField(provided = true) + ListStore<Position> store; + + @UiField + Grid<Position> grid; + + public ArchiveView(ArchiveHandler archiveHandler) { + this.archiveHandler = archiveHandler; + + PositionProperties positionProperties = GWT.create(PositionProperties.class); + + List<ColumnConfig<Position, ?>> columnConfigList = new LinkedList<ColumnConfig<Position, ?>>(); + + columnConfigList.add(new ColumnConfig<Position, Boolean>(positionProperties.valid(), 0, "Valid")); + + ColumnConfig<Position, Date> columnConfig = new ColumnConfig<Position, Date>(positionProperties.time(), 0, "Time"); + columnConfig.setCell(new DateCell(DateTimeFormat.getFormat("yyyy-MM-dd HH:mm:ss"))); + columnConfigList.add(columnConfig); + + columnConfigList.add(new ColumnConfig<Position, Double>(positionProperties.latitude(), 0, "Latitude")); + columnConfigList.add(new ColumnConfig<Position, Double>(positionProperties.longitude(), 0, "Longitude")); + columnConfigList.add(new ColumnConfig<Position, Double>(positionProperties.altitude(), 0, "Altitude")); + columnConfigList.add(new ColumnConfig<Position, Double>(positionProperties.speed(), 0, "Speed")); + columnConfigList.add(new ColumnConfig<Position, Double>(positionProperties.course(), 0, "Course")); + columnConfigList.add(new ColumnConfig<Position, Double>(positionProperties.power(), 0, "Power")); + + columnModel = new ColumnModel<Position>(columnConfigList); + + store = new ListStore<Position>(positionProperties.id()); + + uiBinder.createAndBindUi(this); + } + +} diff --git a/src/org/traccar/web/client/view/ArchiveView.ui.xml b/src/org/traccar/web/client/view/ArchiveView.ui.xml new file mode 100644 index 00000000..370eb024 --- /dev/null +++ b/src/org/traccar/web/client/view/ArchiveView.ui.xml @@ -0,0 +1,60 @@ +<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent"> +<ui:UiBinder + xmlns:ui="urn:ui:com.google.gwt.uibinder" + xmlns:g="urn:import:com.google.gwt.user.client.ui" + xmlns:container="urn:import:com.sencha.gxt.widget.core.client.container" + xmlns:gxt="urn:import:com.sencha.gxt.widget.core.client" + xmlns:toolbar="urn:import:com.sencha.gxt.widget.core.client.toolbar" + xmlns:grid="urn:import:com.sencha.gxt.widget.core.client.grid" + xmlns:button="urn:import:com.sencha.gxt.widget.core.client.button" + xmlns:form="urn:import:com.sencha.gxt.widget.core.client.form"> + + <ui:with type="com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer.VerticalLayoutData" field="toolBarRowData"> + <ui:attributes width="1" height="-1" /> + </ui:with> + <ui:with type="com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer.VerticalLayoutData" field="layoutData"> + <ui:attributes width="1" height="1" /> + </ui:with> + + <ui:with type="com.sencha.gxt.data.shared.ListStore" field="store" /> + <ui:with type="com.sencha.gxt.widget.core.client.grid.ColumnModel" field="columnModel" /> + + <ui:with type="com.sencha.gxt.widget.core.client.grid.GridView" field="view"> + <ui:attributes stripeRows="true" autoFill="true" /> + </ui:with> + + <gxt:ContentPanel ui:field="contentPanel" headingText="Archive"> + <container:VerticalLayoutContainer> + + <container:child layoutData="{toolBarRowData}"> + <toolbar:ToolBar> + + <toolbar:LabelToolItem label="Device:" /> + <form:TimeField /> <!-- put combobox here --> + <toolbar:SeparatorToolItem /> + + <toolbar:LabelToolItem label="From:" /> + <form:DateField width="125" /> + <toolbar:LabelToolItem width="5" /> + <form:TimeField width="75" /> + <toolbar:SeparatorToolItem /> + + <toolbar:LabelToolItem label="To:" /> + <form:DateField width="125" /> + <toolbar:LabelToolItem width="5" /> + <form:TimeField width="75" /> + <toolbar:SeparatorToolItem /> + + <button:TextButton ui:field="loadButton" text="Load" /> + + </toolbar:ToolBar> + </container:child> + + <container:child layoutData="{layoutData}"> + <grid:Grid ui:field="grid" store="{store}" cm="{columnModel}" view="{view}" /> + </container:child> + + </container:VerticalLayoutContainer> + </gxt:ContentPanel> + +</ui:UiBinder> diff --git a/src/org/traccar/web/client/view/DeviceDialog.java b/src/org/traccar/web/client/view/DeviceDialog.java new file mode 100644 index 00000000..891dd4ef --- /dev/null +++ b/src/org/traccar/web/client/view/DeviceDialog.java @@ -0,0 +1,69 @@ +package org.traccar.web.client.view; + +import org.traccar.web.shared.model.Device; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.editor.client.Editor; +import com.google.gwt.editor.client.SimpleBeanEditorDriver; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.uibinder.client.UiHandler; +import com.google.gwt.user.client.ui.Widget; +import com.sencha.gxt.widget.core.client.Window; +import com.sencha.gxt.widget.core.client.event.SelectEvent; +import com.sencha.gxt.widget.core.client.form.TextField; + +public class DeviceDialog implements Editor<Device> { + + private static DeviceDialogUiBinder uiBinder = GWT.create(DeviceDialogUiBinder.class); + + interface DeviceDialogUiBinder extends UiBinder<Widget, DeviceDialog> { + } + + private DeviceDriver driver = GWT.create(DeviceDriver.class); + + interface DeviceDriver extends SimpleBeanEditorDriver<Device, DeviceDialog> { + } + + public interface DeviceHandler { + public void onSave(Device device); + } + + private DeviceHandler deviceHandler; + + @UiField + Window window; + + @UiField + TextField name; + + @UiField + TextField uniqueId; + + public DeviceDialog(Device device, DeviceHandler deviceHandler) { + this.deviceHandler = deviceHandler; + uiBinder.createAndBindUi(this); + driver.initialize(this); + driver.edit(device); + } + + public void show() { + window.show(); + } + + public void hide() { + window.hide(); + } + + @UiHandler("saveButton") + public void onLoginClicked(SelectEvent event) { + window.hide(); + deviceHandler.onSave(driver.flush()); + } + + @UiHandler("cancelButton") + public void onRegisterClicked(SelectEvent event) { + window.hide(); + } + +} diff --git a/src/org/traccar/web/client/view/DeviceDialog.ui.xml b/src/org/traccar/web/client/view/DeviceDialog.ui.xml new file mode 100644 index 00000000..4f7ccc56 --- /dev/null +++ b/src/org/traccar/web/client/view/DeviceDialog.ui.xml @@ -0,0 +1,40 @@ +<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent"> +<ui:UiBinder + xmlns:ui="urn:ui:com.google.gwt.uibinder" + xmlns:g="urn:import:com.google.gwt.user.client.ui" + xmlns:gxt="urn:import:com.sencha.gxt.widget.core.client" + xmlns:container="urn:import:com.sencha.gxt.widget.core.client.container" + xmlns:form="urn:import:com.sencha.gxt.widget.core.client.form" + xmlns:button="urn:import:com.sencha.gxt.widget.core.client.button"> + + <ui:with type="com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer.VerticalLayoutData" field="verticalLayoutData"> + <ui:attributes width="1" height="-1" /> + </ui:with> + + <gxt:Window ui:field="window" pixelSize="300, 130" modal="true" headingText="Device" focusWidget="{saveButton}"> + <container:VerticalLayoutContainer> + <container:child layoutData="{verticalLayoutData}"> + <form:FieldLabel text="Name"> + <form:widget> + <form:TextField ui:field="name" /> + </form:widget> + </form:FieldLabel> + </container:child> + <container:child layoutData="{verticalLayoutData}"> + <form:FieldLabel text="Unique Identifier"> + <form:widget> + <form:TextField ui:field="uniqueId" /> + </form:widget> + </form:FieldLabel> + </container:child> + </container:VerticalLayoutContainer> + + <gxt:button> + <button:TextButton ui:field="saveButton" text="Save" /> + </gxt:button> + <gxt:button> + <button:TextButton ui:field="cancelButton" text="Cancel" /> + </gxt:button> + </gxt:Window> + +</ui:UiBinder> diff --git a/src/org/traccar/web/client/view/DeviceView.java b/src/org/traccar/web/client/view/DeviceView.java new file mode 100644 index 00000000..157973e0 --- /dev/null +++ b/src/org/traccar/web/client/view/DeviceView.java @@ -0,0 +1,128 @@ +package org.traccar.web.client.view; + +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; + +import org.traccar.web.client.model.DeviceProperties; +import org.traccar.web.shared.model.Device; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.uibinder.client.UiHandler; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.Widget; +import com.sencha.gxt.core.client.Style.SelectionMode; +import com.sencha.gxt.data.shared.ListStore; +import com.sencha.gxt.widget.core.client.ContentPanel; +import com.sencha.gxt.widget.core.client.button.TextButton; +import com.sencha.gxt.widget.core.client.event.SelectEvent; +import com.sencha.gxt.widget.core.client.grid.ColumnConfig; +import com.sencha.gxt.widget.core.client.grid.ColumnModel; +import com.sencha.gxt.widget.core.client.grid.Grid; +import com.sencha.gxt.widget.core.client.selection.SelectionChangedEvent; + +public class DeviceView extends Composite implements SelectionChangedEvent.SelectionChangedHandler<Device> { + + private static DeviceViewUiBinder uiBinder = GWT.create(DeviceViewUiBinder.class); + + interface DeviceViewUiBinder extends UiBinder<Widget, DeviceView> { + } + + public interface DeviceHandler { + public void onSelected(Device device); + public void onAdd(); + public void onEdit(Device device); + public void onRemove(Device device); + } + + private DeviceHandler deviceHandler; + + @UiField + ContentPanel contentPanel; + + public ContentPanel getView() { + return contentPanel; + } + + @UiField + TextButton addButton; + + @UiField + TextButton editButton; + + @UiField + TextButton removeButton; + + @UiField(provided = true) + ColumnModel<Device> columnModel; + + @UiField(provided = true) + ListStore<Device> store; + + @UiField + Grid<Device> grid; + + public DeviceView(DeviceHandler deviceHandler) { + this.deviceHandler = deviceHandler; + + DeviceProperties deviceProperties = GWT.create(DeviceProperties.class); + + List<ColumnConfig<Device, ?>> columnConfigList = new LinkedList<ColumnConfig<Device, ?>>(); + columnConfigList.add(new ColumnConfig<Device, String>(deviceProperties.name(), 0, "Name")); + columnConfigList.add(new ColumnConfig<Device, String>(deviceProperties.uniqueId(), 0, "Unique Identifier")); + columnModel = new ColumnModel<Device>(columnConfigList); + + store = new ListStore<Device>(deviceProperties.id()); + + uiBinder.createAndBindUi(this); + + grid.getSelectionModel().addSelectionChangedHandler(this); + grid.getSelectionModel().setSelectionMode(SelectionMode.SINGLE); + } + + @Override + public void onSelectionChanged(SelectionChangedEvent<Device> event) { + editButton.setEnabled(!event.getSelection().isEmpty()); + removeButton.setEnabled(!event.getSelection().isEmpty()); + + if (event.getSelection().isEmpty()) { + deviceHandler.onSelected(null); + } else { + deviceHandler.onSelected(event.getSelection().get(0)); + } + } + + public void load(Collection<Device> devices) { + store.addAll(devices); + } + + @UiHandler("addButton") + public void onAddClicked(SelectEvent event) { + deviceHandler.onAdd(); + } + + public void add(Device device) { + store.add(store.size(), device); + } + + @UiHandler("editButton") + public void onEditClicked(SelectEvent event) { + deviceHandler.onEdit(grid.getSelectionModel().getSelectedItem()); + } + + public void update(Device device) { + store.update(device); + } + + @UiHandler("removeButton") + public void onRemoveClicked(SelectEvent event) { + deviceHandler.onRemove(grid.getSelectionModel().getSelectedItem()); + } + + public void remove(Device device) { + store.remove(device); + } + +} diff --git a/src/org/traccar/web/client/view/DeviceView.ui.xml b/src/org/traccar/web/client/view/DeviceView.ui.xml new file mode 100644 index 00000000..d9988d5e --- /dev/null +++ b/src/org/traccar/web/client/view/DeviceView.ui.xml @@ -0,0 +1,46 @@ +<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent"> +<ui:UiBinder + xmlns:ui="urn:ui:com.google.gwt.uibinder" + xmlns:g="urn:import:com.google.gwt.user.client.ui" + xmlns:container="urn:import:com.sencha.gxt.widget.core.client.container" + xmlns:gxt="urn:import:com.sencha.gxt.widget.core.client" + xmlns:toolbar="urn:import:com.sencha.gxt.widget.core.client.toolbar" + xmlns:grid="urn:import:com.sencha.gxt.widget.core.client.grid" + xmlns:button="urn:import:com.sencha.gxt.widget.core.client.button"> + + <ui:with type="com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer.VerticalLayoutData" field="toolBarRowData"> + <ui:attributes width="1" height="-1" /> + </ui:with> + <ui:with type="com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer.VerticalLayoutData" field="layoutData"> + <ui:attributes width="1" height="1" /> + </ui:with> + + <ui:with type="com.sencha.gxt.data.shared.ListStore" field="store" /> + <ui:with type="com.sencha.gxt.widget.core.client.grid.ColumnModel" field="columnModel" /> + + <ui:with type="com.sencha.gxt.widget.core.client.grid.GridView" field="view"> + <ui:attributes stripeRows="true" autoFill="true" /> + </ui:with> + + <gxt:ContentPanel ui:field="contentPanel" headingText="Devices"> + <container:VerticalLayoutContainer> + + <container:child layoutData="{toolBarRowData}"> + <toolbar:ToolBar> + <button:TextButton ui:field="addButton" text="Add" /> + <button:TextButton ui:field="editButton" text="Edit" enabled="false" /> + <button:TextButton ui:field="removeButton" text="Remove" enabled="false" /> + <toolbar:FillToolItem /> + <toolbar:SeparatorToolItem /> + <button:TextButton ui:field="settingsButton" text="Settings" enabled="false" /> + </toolbar:ToolBar> + </container:child> + + <container:child layoutData="{layoutData}"> + <grid:Grid ui:field="grid" store="{store}" cm="{columnModel}" view="{view}" /> + </container:child> + + </container:VerticalLayoutContainer> + </gxt:ContentPanel> + +</ui:UiBinder> diff --git a/src/org/traccar/web/client/view/LoginDialog.java b/src/org/traccar/web/client/view/LoginDialog.java new file mode 100644 index 00000000..44474179 --- /dev/null +++ b/src/org/traccar/web/client/view/LoginDialog.java @@ -0,0 +1,59 @@ +package org.traccar.web.client.view; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.uibinder.client.UiHandler; +import com.google.gwt.user.client.ui.Widget; +import com.sencha.gxt.widget.core.client.Window; +import com.sencha.gxt.widget.core.client.event.SelectEvent; +import com.sencha.gxt.widget.core.client.form.PasswordField; +import com.sencha.gxt.widget.core.client.form.TextField; + +public class LoginDialog { + + private static LoginDialogUiBinder uiBinder = GWT.create(LoginDialogUiBinder.class); + + interface LoginDialogUiBinder extends UiBinder<Widget, LoginDialog> { + } + + public interface LoginHandler { + public void onLogin(String login, String password); + public void onRegister(String login, String password); + } + + private LoginHandler loginHandler; + + @UiField + Window window; + + @UiField + TextField login; + + @UiField + PasswordField password; + + public LoginDialog(LoginHandler loginHandler) { + this.loginHandler = loginHandler; + uiBinder.createAndBindUi(this); + } + + public void show() { + window.show(); + } + + public void hide() { + window.hide(); + } + + @UiHandler("loginButton") + public void onLoginClicked(SelectEvent event) { + loginHandler.onLogin(login.getText(), password.getText()); + } + + @UiHandler("registerButton") + public void onRegisterClicked(SelectEvent event) { + loginHandler.onRegister(login.getText(), password.getText()); + } + +} diff --git a/src/org/traccar/web/client/view/LoginDialog.ui.xml b/src/org/traccar/web/client/view/LoginDialog.ui.xml new file mode 100644 index 00000000..8c31b308 --- /dev/null +++ b/src/org/traccar/web/client/view/LoginDialog.ui.xml @@ -0,0 +1,40 @@ +<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent"> +<ui:UiBinder + xmlns:ui="urn:ui:com.google.gwt.uibinder" + xmlns:g="urn:import:com.google.gwt.user.client.ui" + xmlns:gxt="urn:import:com.sencha.gxt.widget.core.client" + xmlns:container="urn:import:com.sencha.gxt.widget.core.client.container" + xmlns:form="urn:import:com.sencha.gxt.widget.core.client.form" + xmlns:button="urn:import:com.sencha.gxt.widget.core.client.button"> + + <ui:with type="com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer.VerticalLayoutData" field="verticalLayoutData"> + <ui:attributes width="1" height="-1" /> + </ui:with> + + <gxt:Window ui:field="window" pixelSize="300, 130" modal="true" closable="false" headingText="Authentication" focusWidget="{loginButton}"> + <container:VerticalLayoutContainer> + <container:child layoutData="{verticalLayoutData}"> + <form:FieldLabel text="User"> + <form:widget> + <form:TextField ui:field="login" /> + </form:widget> + </form:FieldLabel> + </container:child> + <container:child layoutData="{verticalLayoutData}"> + <form:FieldLabel text="Password"> + <form:widget> + <form:PasswordField ui:field="password" /> + </form:widget> + </form:FieldLabel> + </container:child> + </container:VerticalLayoutContainer> + + <gxt:button> + <button:TextButton ui:field="loginButton" text="Login" /> + </gxt:button> + <gxt:button> + <button:TextButton ui:field="registerButton" text="Register" /> + </gxt:button> + </gxt:Window> + +</ui:UiBinder> diff --git a/src/org/traccar/web/client/view/MapView.java b/src/org/traccar/web/client/view/MapView.java new file mode 100644 index 00000000..af38c443 --- /dev/null +++ b/src/org/traccar/web/client/view/MapView.java @@ -0,0 +1,149 @@ +package org.traccar.web.client.view; + +import java.util.AbstractMap; +import java.util.HashMap; +import java.util.List; + +import org.gwtopenmaps.openlayers.client.Icon; +import org.gwtopenmaps.openlayers.client.LonLat; +import org.gwtopenmaps.openlayers.client.Map; +import org.gwtopenmaps.openlayers.client.MapOptions; +import org.gwtopenmaps.openlayers.client.MapWidget; +import org.gwtopenmaps.openlayers.client.Marker; +import org.gwtopenmaps.openlayers.client.Pixel; +import org.gwtopenmaps.openlayers.client.Projection; +import org.gwtopenmaps.openlayers.client.Size; +import org.gwtopenmaps.openlayers.client.control.ScaleLine; +import org.gwtopenmaps.openlayers.client.layer.Markers; +import org.gwtopenmaps.openlayers.client.layer.OSM; +import org.traccar.web.shared.model.Device; +import org.traccar.web.shared.model.Position; + +import com.google.gwt.core.client.Scheduler; +import com.google.gwt.event.logical.shared.ResizeEvent; +import com.google.gwt.event.logical.shared.ResizeHandler; +import com.google.gwt.user.client.Command; +import com.sencha.gxt.widget.core.client.ContentPanel; + +class MarkerIconFactory { + + private static final Size iconSize = new Size(21, 25); + private static final Pixel iconOffset = new Pixel(-10.5f, -25.0f); + + private static final String iconUrl = "http://www.openlayers.org/api/img/"; + private static final String iconRed = iconUrl + "marker.png"; + private static final String iconBlue = iconUrl + "marker-blue.png"; + private static final String iconGreen = iconUrl + "marker-green.png"; + private static final String iconGold = iconUrl + "marker-gold.png"; + + public static Icon getLocationIcon() { + return new Icon(iconRed, iconSize, iconOffset); + } + + public static Icon getSelectedLocationIcon() { + return new Icon(iconGreen, iconSize, iconOffset); + } + + public static Icon getArchiveIcon() { + return new Icon(iconBlue, iconSize, iconOffset); + } + + public static Icon getSelectedArchiveIcon() { + return new Icon(iconGold, iconSize, iconOffset); + } + +} + +public class MapView { + + private ContentPanel contentPanel; + + public ContentPanel getView() { + return contentPanel; + } + + private MapWidget mapWidget; + private Map map; + private Markers markerLayer; + + private LonLat createPoint(double longitude, double latitude) { + LonLat point = new LonLat(longitude, latitude); + point.transform(new Projection("EPSG:4326").getProjectionCode(), map.getProjection()); + return point; + } + + public MapView() { + contentPanel = new ContentPanel(); + contentPanel.setHeadingText("Map"); + + MapOptions defaultMapOptions = new MapOptions(); + defaultMapOptions.setNumZoomLevels(16); + + mapWidget = new MapWidget("100%", "100%", defaultMapOptions); + map = mapWidget.getMap(); + + OSM mapLayer = OSM.Mapnik("Mapnik"); + mapLayer.setIsBaseLayer(true); + + markerLayer = new Markers("Markers"); + + map.addLayer(mapLayer); + map.addLayer(markerLayer); + map.addControl(new ScaleLine()); + map.setCenter(createPoint(30, 60), 1); + + contentPanel.add(mapWidget); + + // Update map size + contentPanel.addResizeHandler(new ResizeHandler() { + @Override + public void onResize(ResizeEvent event) { + Scheduler.get().scheduleDeferred(new Command() { + @Override + public void execute() { + map.updateSize(); + } + }); + } + }); + } + + private AbstractMap<Long, Marker> markerMap = new HashMap<Long, Marker>(); + private Device selectedDevice; + + public void showPositions(List<Position> positions) { + markerMap.clear(); + markerLayer.clearMarkers(); + for (Position position : positions) { + Marker marker = new Marker( + createPoint(position.getLongitude(), position.getLatitude()), MarkerIconFactory.getLocationIcon()); + markerMap.put(position.getDevice().getId(), marker); + markerLayer.addMarker(marker); + } + if (selectedDevice != null) { + select(selectedDevice, false); + } + } + + private void changeMarkerIcon(Marker marker, Icon icon) { + Marker newMarker = new Marker(marker.getLonLat(), icon); + markerLayer.removeMarker(marker); + markerLayer.addMarker(newMarker); + } + + public void select(Device device, boolean center) { + if (selectedDevice != null) { + changeMarkerIcon(markerMap.get(selectedDevice.getId()), MarkerIconFactory.getLocationIcon()); + selectedDevice = null; + } + if (device != null && markerMap.containsKey(device.getId())) { + Marker marker = markerMap.get(device.getId()); + if (center) { + map.panTo(marker.getLonLat()); + } + changeMarkerIcon(marker, MarkerIconFactory.getSelectedLocationIcon()); + selectedDevice = device; + } + } + +} diff --git a/src/org/traccar/web/server/model/DataServiceImpl.java b/src/org/traccar/web/server/model/DataServiceImpl.java new file mode 100644 index 00000000..cfcc6226 --- /dev/null +++ b/src/org/traccar/web/server/model/DataServiceImpl.java @@ -0,0 +1,172 @@ +package org.traccar.web.server.model; + +import java.util.Date; +import java.util.LinkedList; +import java.util.List; + +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.persistence.EntityManager; +import javax.persistence.Persistence; +import javax.persistence.TypedQuery; +import javax.servlet.ServletException; +import javax.servlet.http.HttpSession; + +import org.traccar.web.client.model.DataService; +import org.traccar.web.shared.model.Device; +import org.traccar.web.shared.model.Position; +import org.traccar.web.shared.model.User; + +import com.google.gwt.user.server.rpc.RemoteServiceServlet; + +public class DataServiceImpl extends RemoteServiceServlet implements DataService { + + private static final long serialVersionUID = 1; + + private static final String PERSISTENCE_DATASTORE = "java:/DefaultDS"; + private static final String PERSISTENCE_UNIT_DEBUG = "debug"; + private static final String PERSISTENCE_UNIT_RELEASE = "release"; + private static final String ATTRIBUTE_USER = "user"; + + private EntityManager entityManager; + + @Override + public void init() throws ServletException { + super.init(); + + String persistenceUnit; + try { + Context context = new InitialContext(); + context.lookup(PERSISTENCE_DATASTORE); + persistenceUnit = PERSISTENCE_UNIT_RELEASE; + } catch (NamingException e) { + persistenceUnit = PERSISTENCE_UNIT_DEBUG; + } + + entityManager = Persistence.createEntityManagerFactory(persistenceUnit).createEntityManager(); + } + + @Override + public void destroy() { + entityManager.close(); + super.destroy(); + } + + private void setUser(User user) { + HttpSession session = getThreadLocalRequest().getSession(); + session.setAttribute(ATTRIBUTE_USER, user); + } + + private User getUser() { + HttpSession session = getThreadLocalRequest().getSession(); + return (User) session.getAttribute(ATTRIBUTE_USER); + } + + @Override + public boolean authenticated() { + return (getUser() != null); + } + + @Override + public boolean authenticate(String login, String password) { + TypedQuery<User> query = entityManager.createQuery( + "SELECT x FROM User x WHERE x.login = :login", User.class); + query.setParameter("login", login); + List<User> results = query.getResultList(); + + if (!results.isEmpty() && password.equals(results.get(0).getPassword())) { + setUser(results.get(0)); + return true; + } + return false; + } + + @Override + public boolean register(String login, String password) { + User user = new User(); + user.setLogin(login); + user.setPassword(password); + entityManager.getTransaction().begin(); + try { + entityManager.persist(user); + } finally { + entityManager.getTransaction().commit(); + } + setUser(user); + return true; + } + + @Override + public List<Device> getDevices() { + User user = getUser(); + List<Device> devices = new LinkedList<Device>(); + devices.addAll(user.getDevices()); + return devices; + } + + @Override + public Device addDevice(Device device) { + User user = getUser(); + entityManager.getTransaction().begin(); + try { + entityManager.persist(device); + user.getDevices().add(device); + } finally { + entityManager.getTransaction().commit(); + } + return device; + } + + @Override + public Device updateDevice(Device device) { + entityManager.getTransaction().begin(); + try { + device = entityManager.merge(device); + } finally { + entityManager.getTransaction().commit(); + } + return device; + } + + @Override + public Device removeDevice(Device device) { + User user = getUser(); + entityManager.getTransaction().begin(); + try { + device = entityManager.merge(device); + user.getDevices().remove(device); + entityManager.remove(device); + } finally { + entityManager.getTransaction().commit(); + } + return device; + } + + @Override + public List<Position> getPositions(Device device, Date from, Date to) { + List<Position> positions = new LinkedList<Position>(); + TypedQuery<Position> query = entityManager.createQuery( + "SELECT x FROM Position x WHERE x.device = :device AND x.time BETWEEN :from AND :to", Position.class); + query.setParameter("device", device); + query.setParameter("from", from); + query.setParameter("to", to); + positions.addAll(query.getResultList()); + return positions; + } + + @Override + public List<Position> getLatestPositions() { + List<Position> positions = new LinkedList<Position>(); + User user = getUser(); + if (!user.getDevices().isEmpty()) { + TypedQuery<Position> query = entityManager.createQuery( + "SELECT x FROM Position x WHERE (x.device, x.time) IN (" + + "SELECT y.device, MAX(y.time) FROM Position y WHERE y.device IN :devices GROUP BY y.device)", Position.class); + query.setParameter("devices", user.getDevices()); + positions.addAll(query.getResultList()); + } + return positions; + } + +} diff --git a/src/org/traccar/web/shared/model/Device.java b/src/org/traccar/web/shared/model/Device.java new file mode 100644 index 00000000..8ae67c34 --- /dev/null +++ b/src/org/traccar/web/shared/model/Device.java @@ -0,0 +1,56 @@ +package org.traccar.web.shared.model; + +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name = "devices") +public class Device implements Serializable { + + private static final long serialVersionUID = 1; + + public Device() { + } + + public Device(Device device) { + id = device.id; + uniqueId = device.uniqueId; + name = device.name; + } + + @Id + @GeneratedValue + private long id; + + public long getId() { + return id; + } + + @Column(unique = true) + private String uniqueId; + + public void setUniqueId(String uniqueId) { + this.uniqueId = uniqueId; + } + + public String getUniqueId() { + return uniqueId; + } + + @Column(unique = true) + private String name; + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + +} diff --git a/src/org/traccar/web/shared/model/Position.java b/src/org/traccar/web/shared/model/Position.java new file mode 100644 index 00000000..13827301 --- /dev/null +++ b/src/org/traccar/web/shared/model/Position.java @@ -0,0 +1,115 @@ +package org.traccar.web.shared.model; + +import java.io.Serializable; +import java.util.Date; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +import org.hibernate.annotations.Index; + +@Entity +@Table(name = "positions") +public class Position implements Serializable, Cloneable { + + private static final long serialVersionUID = 1; + + public Position() { + } + + public Position(Position position) { + id = position.id; + device = position.device; + time = position.time; + valid = position.valid; + latitude = position.latitude; + longitude = position.longitude; + altitude = position.altitude; + speed = position.speed; + course = position.course; + power = position.power; + address = position.address; + other = position.other; + } + + @Id + @GeneratedValue + private long id; + + public long getId() { + return id; + } + + @ManyToOne + @Index(name = "positionsIndex") + private Device device; + + public Device getDevice() { + return device; + } + + @Index(name = "positionsIndex") + private Date time; + + public Date getTime() { + return time; + } + + private boolean valid; + + public boolean getValid() { + return valid; + } + + private double latitude; + + public double getLatitude() { + return latitude; + } + + private double longitude; + + public double getLongitude() { + return longitude; + } + + private double altitude; + + public double getAltitude() { + return altitude; + } + + private double speed; + + public double getSpeed() { + return speed; + } + + private double course; + + public double getCourse() { + return course; + } + + private double power; + + public double getPower() { + return power; + } + + private String address; + + public String getAddress() { + return address; + } + + private String other; + + public String getOther() { + return other; + } + +} diff --git a/src/org/traccar/web/shared/model/User.java b/src/org/traccar/web/shared/model/User.java new file mode 100644 index 00000000..9064f190 --- /dev/null +++ b/src/org/traccar/web/shared/model/User.java @@ -0,0 +1,65 @@ +package org.traccar.web.shared.model; + +import java.io.Serializable; +import java.util.LinkedList; +import java.util.List; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import javax.persistence.Table; + +@Entity +@Table(name="users") +public class User implements Serializable, Cloneable { + + private static final long serialVersionUID = 1; + + public User() { + } + + public User(User user) { + id = user.id; + login = user.login; + password = user.password; + } + + @Id + @GeneratedValue + private long id; + + public long getId() { + return id; + } + + @Column(unique = true) + private String login; + + public void setLogin(String login) { + this.login = login; + } + + public String getLogin() { + return login; + } + + private String password; + + public void setPassword(String password) { + this.password = password; + } + + public String getPassword() { + return password; + } + + @OneToMany + private List<Device> devices = new LinkedList<Device>(); + + public List<Device> getDevices() { + return devices; + } + +} |