diff --git a/bundles/core/org.eclipse.smarthome.core.persistence/META-INF/MANIFEST.MF b/bundles/core/org.eclipse.smarthome.core.persistence/META-INF/MANIFEST.MF
index 0d6c7732fd1..f5c4e808829 100644
--- a/bundles/core/org.eclipse.smarthome.core.persistence/META-INF/MANIFEST.MF
+++ b/bundles/core/org.eclipse.smarthome.core.persistence/META-INF/MANIFEST.MF
@@ -11,5 +11,6 @@ Import-Package: org.eclipse.smarthome.core.items,
org.eclipse.smarthome.core.persistence,
org.eclipse.smarthome.core.types,
org.slf4j
-Export-Package: org.eclipse.smarthome.core.persistence
+Export-Package: org.eclipse.smarthome.core.persistence,
+ org.eclipse.smarthome.core.persistence.dto
Bundle-ClassPath: .
diff --git a/bundles/core/org.eclipse.smarthome.core.persistence/src/main/java/org/eclipse/smarthome/core/persistence/HistoricItem.java b/bundles/core/org.eclipse.smarthome.core.persistence/src/main/java/org/eclipse/smarthome/core/persistence/HistoricItem.java
index 723ba2a7ecb..8b0d567a039 100644
--- a/bundles/core/org.eclipse.smarthome.core.persistence/src/main/java/org/eclipse/smarthome/core/persistence/HistoricItem.java
+++ b/bundles/core/org.eclipse.smarthome.core.persistence/src/main/java/org/eclipse/smarthome/core/persistence/HistoricItem.java
@@ -27,23 +27,23 @@ public interface HistoricItem {
/**
* returns the timestamp of the persisted item
- *
+ *
* @return the timestamp of the item
*/
Date getTimestamp();
/**
* returns the current state of the item
- *
+ *
* @return the current state
*/
- public State getState();
+ State getState();
/**
* returns the name of the item
- *
+ *
* @return the name of the item
*/
- public String getName();
+ String getName();
}
diff --git a/bundles/core/org.eclipse.smarthome.core.persistence/src/main/java/org/eclipse/smarthome/core/persistence/ModifiablePersistenceService.java b/bundles/core/org.eclipse.smarthome.core.persistence/src/main/java/org/eclipse/smarthome/core/persistence/ModifiablePersistenceService.java
new file mode 100644
index 00000000000..777589cb665
--- /dev/null
+++ b/bundles/core/org.eclipse.smarthome.core.persistence/src/main/java/org/eclipse/smarthome/core/persistence/ModifiablePersistenceService.java
@@ -0,0 +1,54 @@
+/**
+ * Copyright (c) 2014-2016 by the respective copyright holders.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.eclipse.smarthome.core.persistence;
+
+import java.util.Date;
+
+import org.eclipse.smarthome.core.items.Item;
+import org.eclipse.smarthome.core.types.State;
+
+/**
+ * This class provides an interface to the a {@link PersistenceService} to allow data to be stored
+ * at a specific time. This allows bindings that interface to devices that store data internally,
+ * and then periodically provide it to the server to be accommodated.
+ *
+ * @author Chris Jackson - Initial implementation and API
+ *
+ */
+public interface ModifiablePersistenceService extends QueryablePersistenceService {
+ /**
+ *
+ * Stores the historic item value. This allows the item, time and value to be specified.
+ *
+ *
+ * Adding data with the same time as an existing record should update the current record value rather than adding a
+ * new record.
+ *
+ *
+ * Implementors should keep in mind that all registered {@link PersistenceService}s are called synchronously. Hence
+ * long running operations should be processed asynchronously. E.g. store
adds things to a queue which
+ * is processed by some asynchronous workers (Quartz Job, Thread, etc.).
+ *
+ *
+ * @param item the data to be stored
+ * @param date the date of the record
+ * @param state the state to be recorded
+ */
+ void store(Item item, Date date, State state);
+
+ /**
+ * Removes data associated with an item from a persistence service.
+ * If all data is removed for the specified item, the persistence service should free any resources associated with
+ * the item (eg. remove any tables or delete files from the storage).
+ *
+ * @param filter the filter to apply to the data removal. ItemName can not be null.
+ * @return true if the query executed successfully
+ * @throws {@link IllegalArgumentException} if item name is null.
+ */
+ boolean remove(FilterCriteria filter) throws IllegalArgumentException;
+}
diff --git a/bundles/core/org.eclipse.smarthome.core.persistence/src/main/java/org/eclipse/smarthome/core/persistence/PersistenceItemInfo.java b/bundles/core/org.eclipse.smarthome.core.persistence/src/main/java/org/eclipse/smarthome/core/persistence/PersistenceItemInfo.java
new file mode 100644
index 00000000000..472d42a5a44
--- /dev/null
+++ b/bundles/core/org.eclipse.smarthome.core.persistence/src/main/java/org/eclipse/smarthome/core/persistence/PersistenceItemInfo.java
@@ -0,0 +1,54 @@
+/**
+ * Copyright (c) 2014-2016 by the respective copyright holders.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.eclipse.smarthome.core.persistence;
+
+import java.util.Date;
+
+/**
+ * This class provides information about an item that is stored in a persistence service.
+ * It is used to return information about the item to the system
+ *
+ * @author Chris Jackson - Initial contribution
+ *
+ */
+public interface PersistenceItemInfo {
+ /**
+ * Returns the item name.
+ * It should be noted that the item name is as stored in the persistence service and as such may not be linked to an
+ * item. This may be the case if the item was removed from the system, but data still exists in the persistence
+ * service.
+ *
+ * @return Item name
+ */
+ String getName();
+
+ /**
+ * Returns a counter with the number of rows of data associated with the item
+ * Note that this should be used as a guide to the amount of data and may note be 100% accurate. If accurate
+ * information is required, the {@link QueryablePersistenceService#query} method should be used.
+ *
+ * @return count of the number of rows of data. May return null if the persistence service doesn't support this.
+ */
+ Integer getCount();
+
+ /**
+ * Returns the earliest timestamp from data in the persistence database
+ *
+ * @return the earliest {@link Date} stored in the database. May return null if the persistence service doesn't
+ * support this.
+ */
+ Date getEarliest();
+
+ /**
+ * Returns the latest timestamp from data in the persistence database
+ *
+ * @return the latest {@link Date} stored in the database. May return null if the persistence service doesn't
+ * support this.
+ */
+ Date getLatest();
+}
diff --git a/bundles/core/org.eclipse.smarthome.core.persistence/src/main/java/org/eclipse/smarthome/core/persistence/PersistenceService.java b/bundles/core/org.eclipse.smarthome.core.persistence/src/main/java/org/eclipse/smarthome/core/persistence/PersistenceService.java
index d8d417ef57d..30e23323565 100644
--- a/bundles/core/org.eclipse.smarthome.core.persistence/src/main/java/org/eclipse/smarthome/core/persistence/PersistenceService.java
+++ b/bundles/core/org.eclipse.smarthome.core.persistence/src/main/java/org/eclipse/smarthome/core/persistence/PersistenceService.java
@@ -7,6 +7,8 @@
*/
package org.eclipse.smarthome.core.persistence;
+import java.util.Locale;
+
import org.eclipse.smarthome.core.items.Item;
/**
@@ -20,12 +22,22 @@
public interface PersistenceService {
/**
- * Returns the name of this {@link PersistenceService}.
- * This name is used to uniquely identify the {@link PersistenceService}.
- *
- * @return the name to uniquely identify the {@link PersistenceService}.
+ * Returns the id of this {@link PersistenceService}.
+ * This id is used to uniquely identify the {@link PersistenceService}.
+ *
+ * @return the id to uniquely identify the {@link PersistenceService}.
+ */
+ String getId();
+
+ /**
+ * Returns the label of this {@link PersistenceService}.
+ * This label provides a user friendly name for the {@link PersistenceService}.
+ *
+ * @param locale the language to return the label in, or null for the default language
+ *
+ * @return the label of the {@link PersistenceService}.
*/
- String getName();
+ String getLabel(Locale locale);
/**
* Stores the current value of the given item.
@@ -34,7 +46,7 @@ public interface PersistenceService {
* long running operations should be processed asynchronously. E.g. store
adds things to a queue which
* is processed by some asynchronous workers (Quartz Job, Thread, etc.).
*
- *
+ *
* @param item the item which state should be persisted.
*/
void store(Item item);
@@ -48,7 +60,7 @@ public interface PersistenceService {
* long running operations should be processed asynchronously. E.g. store
adds things to a queue which
* is processed by some asynchronous workers (Quartz Job, Thread, etc.).
*
- *
+ *
* @param item the item which state should be persisted.
* @param alias the alias under which the item should be persisted.
*/
diff --git a/bundles/core/org.eclipse.smarthome.core.persistence/src/main/java/org/eclipse/smarthome/core/persistence/QueryablePersistenceService.java b/bundles/core/org.eclipse.smarthome.core.persistence/src/main/java/org/eclipse/smarthome/core/persistence/QueryablePersistenceService.java
index edcc758f651..9986f28527d 100644
--- a/bundles/core/org.eclipse.smarthome.core.persistence/src/main/java/org/eclipse/smarthome/core/persistence/QueryablePersistenceService.java
+++ b/bundles/core/org.eclipse.smarthome.core.persistence/src/main/java/org/eclipse/smarthome/core/persistence/QueryablePersistenceService.java
@@ -7,20 +7,34 @@
*/
package org.eclipse.smarthome.core.persistence;
+import java.util.Set;
+
+import org.eclipse.smarthome.core.items.Item;
+
/**
* A queryable persistence service which can be used to store and retrieve
* data from openHAB. This is most likely some kind of database system.
*
* @author Kai Kreuzer - Initial contribution and API
+ * @author Chris Jackson - Added getItems method
*/
public interface QueryablePersistenceService extends PersistenceService {
/**
* Queries the {@link PersistenceService} for data with a given filter criteria
- *
+ *
* @param filter the filter to apply to the query
* @return a time series of items
*/
Iterable query(FilterCriteria filter);
+ /**
+ * Returns a list of items that are stored in the persistence service
+ *
+ * This is returned as a string to allow the persistence service to return items that are no long available as an
+ * ESH {@link Item}.
+ *
+ * @return list of strings of item names contained in the store. Not null.
+ */
+ Set getItemInfo();
}
diff --git a/bundles/io/org.eclipse.smarthome.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/persistence/ItemHistoryBean.java b/bundles/core/org.eclipse.smarthome.core.persistence/src/main/java/org/eclipse/smarthome/core/persistence/dto/ItemHistoryDTO.java
similarity index 91%
rename from bundles/io/org.eclipse.smarthome.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/persistence/ItemHistoryBean.java
rename to bundles/core/org.eclipse.smarthome.core.persistence/src/main/java/org/eclipse/smarthome/core/persistence/dto/ItemHistoryDTO.java
index 92aa189cdda..7092276b1dc 100644
--- a/bundles/io/org.eclipse.smarthome.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/persistence/ItemHistoryBean.java
+++ b/bundles/core/org.eclipse.smarthome.core.persistence/src/main/java/org/eclipse/smarthome/core/persistence/dto/ItemHistoryDTO.java
@@ -5,7 +5,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
-package org.eclipse.smarthome.io.rest.core.persistence;
+package org.eclipse.smarthome.core.persistence.dto;
import java.util.ArrayList;
import java.util.List;
@@ -18,7 +18,7 @@
* @author Chris Jackson - Initial Contribution
*
*/
-public class ItemHistoryBean {
+public class ItemHistoryDTO {
public String name;
public String totalrecords;
@@ -26,7 +26,7 @@ public class ItemHistoryBean {
public List data = new ArrayList();
- public ItemHistoryBean() {
+ public ItemHistoryDTO() {
};
/**
diff --git a/bundles/io/org.eclipse.smarthome.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/persistence/ServiceBean.java b/bundles/core/org.eclipse.smarthome.core.persistence/src/main/java/org/eclipse/smarthome/core/persistence/dto/PersistenceServiceDTO.java
similarity index 50%
rename from bundles/io/org.eclipse.smarthome.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/persistence/ServiceBean.java
rename to bundles/core/org.eclipse.smarthome.core.persistence/src/main/java/org/eclipse/smarthome/core/persistence/dto/PersistenceServiceDTO.java
index 1b67273ae6a..b2897bc072d 100644
--- a/bundles/io/org.eclipse.smarthome.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/persistence/ServiceBean.java
+++ b/bundles/core/org.eclipse.smarthome.core.persistence/src/main/java/org/eclipse/smarthome/core/persistence/dto/PersistenceServiceDTO.java
@@ -5,17 +5,30 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
-package org.eclipse.smarthome.io.rest.core.persistence;
+package org.eclipse.smarthome.core.persistence.dto;
/**
- * This is a java bean that is used to serialize items to JSON.
+ * This is a java bean that is used to serialize services to JSON.
*
* @author Chris Jackson - Initial Contribution
*
*/
-public class ServiceBean {
- ServiceBean() {
+public class PersistenceServiceDTO {
+ public PersistenceServiceDTO() {
}
- public String name;
+ /**
+ * Service Id
+ */
+ public String id;
+
+ /**
+ * Service label
+ */
+ public String label;
+
+ /**
+ * Persistence service class
+ */
+ public String type;
}
diff --git a/bundles/io/org.eclipse.smarthome.io.rest.core/META-INF/MANIFEST.MF b/bundles/io/org.eclipse.smarthome.io.rest.core/META-INF/MANIFEST.MF
index 9922b6f9c4d..a08c4d09e2a 100644
--- a/bundles/io/org.eclipse.smarthome.io.rest.core/META-INF/MANIFEST.MF
+++ b/bundles/io/org.eclipse.smarthome.io.rest.core/META-INF/MANIFEST.MF
@@ -29,6 +29,7 @@ Import-Package: com.google.common.base,
org.eclipse.smarthome.core.library.items,
org.eclipse.smarthome.core.library.types,
org.eclipse.smarthome.core.persistence,
+ org.eclipse.smarthome.core.persistence.dto,
org.eclipse.smarthome.core.thing,
org.eclipse.smarthome.core.thing.dto,
org.eclipse.smarthome.core.thing.link,
@@ -40,6 +41,7 @@ Import-Package: com.google.common.base,
org.eclipse.smarthome.io.rest,
org.eclipse.smarthome.io.rest.core.item,
org.eclipse.smarthome.io.rest.core.thing,
+ org.eclipse.smarthome.model.persistence.extensions,
org.osgi.framework,
org.osgi.service.cm,
org.osgi.service.component,
diff --git a/bundles/io/org.eclipse.smarthome.io.rest.core/OSGI-INF/persistenceresource.xml b/bundles/io/org.eclipse.smarthome.io.rest.core/OSGI-INF/persistenceresource.xml
index 2fe512ed8ec..32cdbe10636 100644
--- a/bundles/io/org.eclipse.smarthome.io.rest.core/OSGI-INF/persistenceresource.xml
+++ b/bundles/io/org.eclipse.smarthome.io.rest.core/OSGI-INF/persistenceresource.xml
@@ -11,6 +11,7 @@
+
diff --git a/bundles/io/org.eclipse.smarthome.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/persistence/ItemHistoryListBean.java b/bundles/io/org.eclipse.smarthome.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/persistence/ItemHistoryListDTO.java
similarity index 69%
rename from bundles/io/org.eclipse.smarthome.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/persistence/ItemHistoryListBean.java
rename to bundles/io/org.eclipse.smarthome.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/persistence/ItemHistoryListDTO.java
index 1219097c5d6..3ac53ecf954 100644
--- a/bundles/io/org.eclipse.smarthome.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/persistence/ItemHistoryListBean.java
+++ b/bundles/io/org.eclipse.smarthome.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/persistence/ItemHistoryListDTO.java
@@ -11,19 +11,21 @@
import java.util.Collection;
import java.util.List;
+import org.eclipse.smarthome.core.persistence.dto.ItemHistoryDTO;
+
/**
* This is a java bean that is used to serialize item lists.
*
* @author Chris Jackson - Initial Contribution
*
*/
-public class ItemHistoryListBean {
- public final List item = new ArrayList();
+public class ItemHistoryListDTO {
+ public final List item = new ArrayList();
- public ItemHistoryListBean() {
+ public ItemHistoryListDTO() {
}
- public ItemHistoryListBean(Collection list) {
+ public ItemHistoryListDTO(Collection list) {
item.addAll(list);
}
}
diff --git a/bundles/io/org.eclipse.smarthome.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/persistence/PersistenceResource.java b/bundles/io/org.eclipse.smarthome.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/persistence/PersistenceResource.java
index a4cc6db1723..0d3155b2811 100644
--- a/bundles/io/org.eclipse.smarthome.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/persistence/PersistenceResource.java
+++ b/bundles/io/org.eclipse.smarthome.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/persistence/PersistenceResource.java
@@ -20,9 +20,13 @@
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
+import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
@@ -33,17 +37,26 @@
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
+import org.eclipse.smarthome.core.items.Item;
+import org.eclipse.smarthome.core.items.ItemNotFoundException;
+import org.eclipse.smarthome.core.items.ItemRegistry;
import org.eclipse.smarthome.core.library.types.DateTimeType;
import org.eclipse.smarthome.core.library.types.OnOffType;
import org.eclipse.smarthome.core.library.types.OpenClosedType;
import org.eclipse.smarthome.core.persistence.FilterCriteria;
import org.eclipse.smarthome.core.persistence.FilterCriteria.Ordering;
import org.eclipse.smarthome.core.persistence.HistoricItem;
+import org.eclipse.smarthome.core.persistence.ModifiablePersistenceService;
import org.eclipse.smarthome.core.persistence.PersistenceService;
import org.eclipse.smarthome.core.persistence.QueryablePersistenceService;
+import org.eclipse.smarthome.core.persistence.dto.ItemHistoryDTO;
+import org.eclipse.smarthome.core.persistence.dto.PersistenceServiceDTO;
import org.eclipse.smarthome.core.types.State;
+import org.eclipse.smarthome.core.types.TypeParser;
import org.eclipse.smarthome.io.rest.JSONResponse;
+import org.eclipse.smarthome.io.rest.LocaleUtil;
import org.eclipse.smarthome.io.rest.RESTResource;
+import org.eclipse.smarthome.model.persistence.extensions.PersistenceExtensions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -57,7 +70,8 @@
* This class acts as a REST resource for history data and provides different methods to interact with the persistence
* store
*
- * @author Chris Jackson - Initial Contribution
+ * @author Chris Jackson - Initial Contribution and add support for ModifiablePersistenceService
+ *
*/
@Path(PersistenceResource.PATH)
@Api(value = PersistenceResource.PATH)
@@ -66,43 +80,110 @@ public class PersistenceResource implements RESTResource {
private final Logger logger = LoggerFactory.getLogger(PersistenceResource.class);
private final int MILLISECONDS_PER_DAY = 86400000;
+ private final String MODIFYABLE = "Modifiable";
+ private final String QUERYABLE = "Queryable";
+ private final String STANDARD = "Standard";
+
// The URI path to this resource
public static final String PATH = "persistence";
- static private Map persistenceServices = new HashMap();
+ private ItemRegistry itemRegistry;
+ private Map persistenceServices = new HashMap();
public void addPersistenceService(PersistenceService service) {
- persistenceServices.put(service.getName(), service);
+ persistenceServices.put(service.getId(), service);
}
public void removePersistenceService(PersistenceService service) {
- persistenceServices.remove(service.getName());
+ persistenceServices.remove(service.getId());
+ }
+
+ protected void setItemRegistry(ItemRegistry itemRegistry) {
+ this.itemRegistry = itemRegistry;
+ }
+
+ protected void unsetItemRegistry(ItemRegistry itemRegistry) {
+ this.itemRegistry = null;
}
@GET
@Produces({ MediaType.APPLICATION_JSON })
@ApiOperation(value = "Gets a list of persistence services.", response = String.class, responseContainer = "List")
- @ApiResponses(value = @ApiResponse(code = 200, message = "OK") )
- public Response httpGetPersistenceServices(@Context HttpHeaders headers) {
- Object responseObject = getPersistenceServiceList();
+ @ApiResponses(value = @ApiResponse(code = 200, message = "OK"))
+ public Response httpGetPersistenceServices(@Context HttpHeaders headers,
+ @HeaderParam(HttpHeaders.ACCEPT_LANGUAGE) @ApiParam(value = HttpHeaders.ACCEPT_LANGUAGE) String language) {
+
+ Locale locale = LocaleUtil.getLocale(language);
+
+ Object responseObject = getPersistenceServiceList(locale);
return Response.ok(responseObject).build();
}
@GET
- @Path("/{itemname: [a-zA-Z_0-9]*}")
+ @Path("/items")
@Produces({ MediaType.APPLICATION_JSON })
- @ApiOperation(value = "Gets item persistence data from the persistence service.", response = ItemHistoryBean.class)
+ @ApiOperation(value = "Gets a list of items available via a specific persistence service.", response = String.class, responseContainer = "List")
+ @ApiResponses(value = @ApiResponse(code = 200, message = "OK"))
+ public Response httpGetPersistenceServiceItems(@Context HttpHeaders headers,
+ @ApiParam(value = "Name of the persistence service. If not provided the default service will be used", required = false) @QueryParam("servicename") String serviceName) {
+
+ return getServiceItemList(serviceName);
+ }
+
+ @GET
+ @Path("/items/{itemname: [a-zA-Z_0-9]*}")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @ApiOperation(value = "Gets item persistence data from the persistence service.", response = ItemHistoryDTO.class)
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK"),
@ApiResponse(code = 404, message = "Unknown Item or persistence service") })
public Response httpGetPersistenceItemData(@Context HttpHeaders headers,
- @ApiParam(value = "The item name", required = true) @PathParam("itemname") String itemName,
@ApiParam(value = "Name of the persistence service. If not provided the default service will be used", required = false) @QueryParam("servicename") String serviceName,
- @ApiParam(value = "Start time of the data to return. Will default to 1 day before endtime", required = false) @QueryParam("starttime") String startTime,
- @ApiParam(value = "End time of the data to return. Will default to current time.", required = false) @QueryParam("endtime") String endTime,
+ @ApiParam(value = "The item name", required = true) @PathParam("itemname") String itemName,
+ @ApiParam(value = "Start time of the data to return. Will default to 1 day before endtime. ["
+ + DateTimeType.DATE_PATTERN_WITH_TZ_AND_MS
+ + "]", required = false) @QueryParam("starttime") String startTime,
+ @ApiParam(value = "End time of the data to return. Will default to current time. ["
+ + DateTimeType.DATE_PATTERN_WITH_TZ_AND_MS
+ + "]", required = false) @QueryParam("endtime") String endTime,
@ApiParam(value = "Page number of data to return. This parameter will enable paging.", required = false) @QueryParam("page") int pageNumber,
- @ApiParam(value = "The length of each page.", required = false) @QueryParam("pagelength") int pageLength) {
+ @ApiParam(value = "The length of each page.", required = false) @QueryParam("pagelength") int pageLength,
+ @ApiParam(value = "Gets one value before and after the requested period.", required = false) @QueryParam("boundary") boolean boundary) {
- return getItemHistoryBean(serviceName, itemName, startTime, endTime, pageNumber, pageLength);
+ return getItemHistoryDTO(serviceName, itemName, startTime, endTime, pageNumber, pageLength, boundary);
+ }
+
+ @DELETE
+ @Path("/items/{itemname: [a-zA-Z_0-9]*}")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @ApiOperation(value = "Delete item data from a specific persistence service.", response = String.class, responseContainer = "List")
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "OK"),
+ @ApiResponse(code = 400, message = "Invalid filter parameters"),
+ @ApiResponse(code = 404, message = "Unknown persistence service") })
+ public Response httpDeletePersistenceServiceItem(@Context HttpHeaders headers,
+ @ApiParam(value = "Name of the persistence service.", required = true) @QueryParam("servicename") String serviceName,
+ @ApiParam(value = "The item name.", required = true) @PathParam("itemname") String itemName,
+ @ApiParam(value = "Start time of the data to return. [" + DateTimeType.DATE_PATTERN_WITH_TZ_AND_MS
+ + "]", required = true) @QueryParam("starttime") String startTime,
+ @ApiParam(value = "End time of the data to return. [" + DateTimeType.DATE_PATTERN_WITH_TZ_AND_MS
+ + "]", required = true) @QueryParam("endtime") String endTime) {
+
+ return deletePersistenceItemData(serviceName, itemName, startTime, endTime);
+ }
+
+ @PUT
+ @Path("/items/{itemname: [a-zA-Z_0-9]*}")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @ApiOperation(value = "Stores item persistence data into the persistence service.", response = ItemHistoryDTO.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "OK"),
+ @ApiResponse(code = 404, message = "Unknown Item or persistence service") })
+ public Response httpPutPersistenceItemData(@Context HttpHeaders headers,
+ @ApiParam(value = "Name of the persistence service. If not provided the default service will be used", required = false) @QueryParam("servicename") String serviceName,
+ @ApiParam(value = "The item name.", required = true) @PathParam("itemname") String itemName,
+ @ApiParam(value = "Time of the data to be stored. Will default to current time. ["
+ + DateTimeType.DATE_PATTERN_WITH_TZ_AND_MS + "]", required = true) @QueryParam("time") String time,
+ @ApiParam(value = "The state to store.", required = true) @QueryParam("state") String value) {
+
+ return putItemState(serviceName, itemName, value, time);
}
private Date convertTime(String sTime) {
@@ -110,27 +191,28 @@ private Date convertTime(String sTime) {
return dateTime.getCalendar().getTime();
}
- private Response getItemHistoryBean(String serviceName, String itemName, String timeBegin, String timeEnd,
- int pageNumber, int pageLength) {
+ private Response getItemHistoryDTO(String serviceName, String itemName, String timeBegin, String timeEnd,
+ int pageNumber, int pageLength, boolean boundary) {
// Benchmarking timer...
long timerStart = System.currentTimeMillis();
// If serviceName is null, then use the default service
PersistenceService service = null;
if (serviceName == null) {
- // TODO: Add handler for default service once this is available in ESH
+ service = persistenceServices.get(PersistenceExtensions.getDefaultService());
} else {
service = persistenceServices.get(serviceName);
}
if (service == null) {
logger.debug("Persistence service not found '{}'.", serviceName);
- return JSONResponse.createErrorResponse(Status.CONFLICT, "Persistence service not found: " + serviceName);
+ return JSONResponse.createErrorResponse(Status.BAD_REQUEST,
+ "Persistence service not found: " + serviceName);
}
if (!(service instanceof QueryablePersistenceService)) {
logger.debug("Persistence service not queryable '{}'.", serviceName);
- return JSONResponse.createErrorResponse(Status.CONFLICT,
+ return JSONResponse.createErrorResponse(Status.BAD_REQUEST,
"Persistence service not queryable: " + serviceName);
}
@@ -165,29 +247,38 @@ private Response getItemHistoryBean(String serviceName, String itemName, String
Long quantity = 0l;
- ItemHistoryBean bean = new ItemHistoryBean();
- bean.name = itemName;
+ ItemHistoryDTO dto = new ItemHistoryDTO();
+ dto.name = itemName;
- // First, get the value at the start time.
- // This is necessary for values that don't change often otherwise data will start after the start of the graph
- // (or not at all if there's no change during the graph period)
filter = new FilterCriteria();
- filter.setEndDate(dateTimeBegin);
filter.setItemName(itemName);
- filter.setPageSize(1);
- filter.setOrdering(Ordering.DESCENDING);
- result = qService.query(filter);
- if (result != null && result.iterator().hasNext()) {
- bean.addData(dateTimeBegin.getTime(), result.iterator().next().getState());
- quantity++;
+
+ // If "boundary" is true then we want to get one value before and after the requested period
+ // This is necessary for values that don't change often otherwise data will start after the start of the graph
+ // (or not at all if there's no change during the graph period)
+ if (boundary) {
+ // Get the value before the start time.
+ filter.setEndDate(dateTimeBegin);
+ filter.setPageSize(1);
+ filter.setOrdering(Ordering.DESCENDING);
+ result = qService.query(filter);
+ if (result != null && result.iterator().hasNext()) {
+ dto.addData(dateTimeBegin.getTime(), result.iterator().next().getState());
+ quantity++;
+ }
+ }
+
+ if (pageLength == 0) {
+ filter.setPageNumber(0);
+ filter.setPageSize(Integer.MAX_VALUE);
+ } else {
+ filter.setPageNumber(pageNumber);
+ filter.setPageSize(pageLength);
}
- filter.setPageSize(pageLength);
- filter.setPageNumber(pageNumber);
filter.setBeginDate(dateTimeBegin);
filter.setEndDate(dateTimeEnd);
filter.setOrdering(Ordering.ASCENDING);
- filter.setPageSize(Integer.MAX_VALUE);
result = qService.query(filter);
if (result != null) {
@@ -201,24 +292,30 @@ private Response getItemHistoryBean(String serviceName, String itemName, String
// For 'binary' states, we need to replicate the data
// to avoid diagonal lines
if (state instanceof OnOffType || state instanceof OpenClosedType) {
- bean.addData(historicItem.getTimestamp().getTime(), state);
+ dto.addData(historicItem.getTimestamp().getTime(), state);
}
- bean.addData(historicItem.getTimestamp().getTime(), state);
+ dto.addData(historicItem.getTimestamp().getTime(), state);
quantity++;
}
+ }
- // Add the last value again at the end time
- if (state != null) {
- bean.addData(dateTimeEnd.getTime(), state);
+ if (boundary) {
+ // Get the value after the end time.
+ filter.setBeginDate(dateTimeEnd);
+ filter.setPageSize(1);
+ filter.setOrdering(Ordering.ASCENDING);
+ result = qService.query(filter);
+ if (result != null && result.iterator().hasNext()) {
+ dto.addData(result.iterator().next().getTimestamp().getTime(), result.iterator().next().getState());
quantity++;
}
}
- bean.datapoints = Long.toString(quantity);
- logger.debug("Persistence returned {} rows in {}ms", bean.datapoints, System.currentTimeMillis() - timerStart);
+ dto.datapoints = Long.toString(quantity);
+ logger.debug("Persistence returned {} rows in {}ms", dto.datapoints, System.currentTimeMillis() - timerStart);
- return JSONResponse.createResponse(Status.OK, bean, "");
+ return JSONResponse.createResponse(Status.OK, dto, "");
}
/**
@@ -226,16 +323,157 @@ private Response getItemHistoryBean(String serviceName, String itemName, String
*
* @return list of persistence services as {@link ServiceBean}
*/
- private List getPersistenceServiceList() {
- List beanList = new ArrayList();
+ private List getPersistenceServiceList(Locale locale) {
+ List dtoList = new ArrayList();
for (Map.Entry service : persistenceServices.entrySet()) {
- ServiceBean serviceBean = new ServiceBean();
- serviceBean.name = service.getKey();
+ PersistenceServiceDTO serviceDTO = new PersistenceServiceDTO();
+ serviceDTO.id = service.getKey();
+ PersistenceService persistence = service.getValue();
+ serviceDTO.label = persistence.getLabel(locale);
+
+ if (persistence instanceof ModifiablePersistenceService) {
+ serviceDTO.type = MODIFYABLE;
+ } else if (persistence instanceof QueryablePersistenceService) {
+ serviceDTO.type = QUERYABLE;
+ } else {
+ serviceDTO.type = STANDARD;
+ }
- beanList.add(serviceBean);
+ dtoList.add(serviceDTO);
}
- return beanList;
+ return dtoList;
+ }
+
+ private Response getServiceItemList(String serviceName) {
+ // If serviceName is null, then use the default service
+ PersistenceService service = null;
+ if (serviceName == null) {
+ serviceName = PersistenceExtensions.getDefaultService();
+ }
+ service = persistenceServices.get(serviceName);
+
+ if (service == null) {
+ logger.debug("Persistence service not found '{}'.", serviceName);
+ return JSONResponse.createErrorResponse(Status.BAD_REQUEST,
+ "Persistence service not found: " + serviceName);
+ }
+
+ if (!(service instanceof QueryablePersistenceService)) {
+ logger.debug("Persistence service not queryable '{}'.", serviceName);
+ return JSONResponse.createErrorResponse(Status.BAD_REQUEST,
+ "Persistence service not queryable: " + serviceName);
+ }
+
+ QueryablePersistenceService qService = (QueryablePersistenceService) service;
+
+ return JSONResponse.createResponse(Status.OK, qService.getItemInfo(), "");
+ }
+
+ private Response deletePersistenceItemData(String serviceName, String itemName, String timeBegin, String timeEnd) {
+ // For deleting, we must specify a servicename - don't use the default service
+ if (serviceName == null || serviceName.length() == 0) {
+ logger.debug("Persistence service must be specified for delete operations.");
+ return JSONResponse.createErrorResponse(Status.BAD_REQUEST,
+ "Persistence service must be specified for delete operations.");
+ }
+
+ PersistenceService service = persistenceServices.get(serviceName);
+ if (service == null) {
+ logger.debug("Persistence service not found '{}'.", serviceName);
+ return JSONResponse.createErrorResponse(Status.BAD_REQUEST,
+ "Persistence service not found: " + serviceName);
+ }
+
+ if (!(service instanceof ModifiablePersistenceService)) {
+ logger.warn("Persistence service not modifiable '{}'.", serviceName);
+ return JSONResponse.createErrorResponse(Status.BAD_REQUEST,
+ "Persistence service not modifiable: " + serviceName);
+ }
+
+ ModifiablePersistenceService mService = (ModifiablePersistenceService) service;
+
+ if (timeBegin == null | timeEnd == null) {
+ return JSONResponse.createErrorResponse(Status.BAD_REQUEST, "The start and end time must be set");
+ }
+
+ Date dateTimeBegin = convertTime(timeBegin);
+ Date dateTimeEnd = convertTime(timeEnd);
+ if (dateTimeEnd.before(dateTimeBegin)) {
+ return JSONResponse.createErrorResponse(Status.BAD_REQUEST, "Start time must be earlier than end time");
+ }
+
+ FilterCriteria filter;
+
+ // First, get the value at the start time.
+ // This is necessary for values that don't change often otherwise data will start after the start of the graph
+ // (or not at all if there's no change during the graph period)
+ filter = new FilterCriteria();
+ filter.setBeginDate(dateTimeBegin);
+ filter.setEndDate(dateTimeEnd);
+ filter.setItemName(itemName);
+ try {
+ mService.remove(filter);
+ } catch (IllegalArgumentException e) {
+ return JSONResponse.createErrorResponse(Status.BAD_REQUEST, "Invalid filter parameters.");
+ }
+
+ return Response.status(Status.OK).build();
+ }
+
+ private Response putItemState(String serviceName, String itemName, String value, String time) {
+ // If serviceName is null, then use the default service
+ PersistenceService service = null;
+ if (serviceName == null) {
+ serviceName = PersistenceExtensions.getDefaultService();
+ }
+ service = persistenceServices.get(serviceName);
+
+ if (service == null) {
+ logger.warn("Persistence service not found '{}'.", serviceName);
+ return JSONResponse.createErrorResponse(Status.BAD_REQUEST,
+ "Persistence service not found: " + serviceName);
+ }
+
+ Item item;
+ try {
+ if (itemRegistry == null) {
+ logger.warn("Item registry not set.");
+ return JSONResponse.createErrorResponse(Status.CONFLICT, "Item registry not set.");
+ }
+ item = itemRegistry.getItem(itemName);
+ } catch (ItemNotFoundException e) {
+ logger.warn("Item not found '{}'.", itemName);
+ return JSONResponse.createErrorResponse(Status.BAD_REQUEST, "Item not found: " + itemName);
+ }
+
+ // Try to parse a State from the input
+ State state = TypeParser.parseState(item.getAcceptedDataTypes(), value);
+ if (state == null) {
+ // State could not be parsed
+ logger.warn("Can't persist item {} with invalid state '{}'.", itemName, value);
+ return JSONResponse.createErrorResponse(Status.BAD_REQUEST, "State could not be parsed: " + value);
+ }
+
+ Date dateTime = null;
+ if (time != null && time.length() != 0) {
+ dateTime = convertTime(time);
+ }
+ if (dateTime == null || dateTime.getTime() == 0) {
+ logger.warn("Error with persistence store to {}. Time badly formatted {}.", itemName, time);
+ return JSONResponse.createErrorResponse(Status.BAD_REQUEST, "Time badly formatted.");
+ }
+
+ if (!(service instanceof ModifiablePersistenceService)) {
+ logger.warn("Persistence service not modifiable '{}'.", serviceName);
+ return JSONResponse.createErrorResponse(Status.BAD_REQUEST,
+ "Persistence service not modifiable: " + serviceName);
+ }
+
+ ModifiablePersistenceService mService = (ModifiablePersistenceService) service;
+
+ mService.store(item, dateTime, state);
+ return Response.status(Status.OK).build();
}
}
diff --git a/bundles/io/org.eclipse.smarthome.io.rest/src/main/java/org/eclipse/smarthome/io/rest/JSONResponse.java b/bundles/io/org.eclipse.smarthome.io.rest/src/main/java/org/eclipse/smarthome/io/rest/JSONResponse.java
index 9c9a4d23391..28284cbdd33 100644
--- a/bundles/io/org.eclipse.smarthome.io.rest/src/main/java/org/eclipse/smarthome/io/rest/JSONResponse.java
+++ b/bundles/io/org.eclipse.smarthome.io.rest/src/main/java/org/eclipse/smarthome/io/rest/JSONResponse.java
@@ -13,6 +13,7 @@
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.ext.Provider;
+import org.eclipse.smarthome.core.library.types.DateTimeType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -35,7 +36,8 @@ public class JSONResponse {
// also dump stacktrace?
private final static boolean WITH_STACKTRACE = false;
- final static Gson GSON = new GsonBuilder().setPrettyPrinting().create();
+ final static Gson GSON = new GsonBuilder().setDateFormat(DateTimeType.DATE_PATTERN_WITH_TZ_AND_MS)
+ .setPrettyPrinting().create();
/**
* hide ctor a bit from public
diff --git a/bundles/model/org.eclipse.smarthome.model.persistence.tests/src/test/java/org/eclipse/smarthome/model/persistence/tests/TestPersistenceService.java b/bundles/model/org.eclipse.smarthome.model.persistence.tests/src/test/java/org/eclipse/smarthome/model/persistence/tests/TestPersistenceService.java
index 202c8579538..6fa82624123 100644
--- a/bundles/model/org.eclipse.smarthome.model.persistence.tests/src/test/java/org/eclipse/smarthome/model/persistence/tests/TestPersistenceService.java
+++ b/bundles/model/org.eclipse.smarthome.model.persistence.tests/src/test/java/org/eclipse/smarthome/model/persistence/tests/TestPersistenceService.java
@@ -10,12 +10,15 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
+import java.util.Locale;
+import java.util.Set;
import org.eclipse.smarthome.core.items.Item;
import org.eclipse.smarthome.core.library.types.DecimalType;
import org.eclipse.smarthome.core.persistence.FilterCriteria;
import org.eclipse.smarthome.core.persistence.FilterCriteria.Ordering;
import org.eclipse.smarthome.core.persistence.HistoricItem;
+import org.eclipse.smarthome.core.persistence.PersistenceItemInfo;
import org.eclipse.smarthome.core.persistence.QueryablePersistenceService;
import org.eclipse.smarthome.core.types.State;
@@ -27,7 +30,7 @@
public class TestPersistenceService implements QueryablePersistenceService {
@Override
- public String getName() {
+ public String getId() {
return "test";
}
@@ -45,13 +48,16 @@ public Iterable query(FilterCriteria filter) {
int startValue = 1950;
int endValue = 2012;
- if (filter.getBeginDate() != null)
+ if (filter.getBeginDate() != null) {
startValue = filter.getBeginDate().getYear() + 1900;
- if (filter.getEndDate() != null)
+ }
+ if (filter.getEndDate() != null) {
endValue = filter.getEndDate().getYear() + 1900;
+ }
- if (endValue <= startValue || startValue < 1950)
+ if (endValue <= startValue || startValue < 1950) {
return Collections.emptyList();
+ }
ArrayList results = new ArrayList(endValue - startValue);
for (int i = startValue; i <= endValue; i++) {
@@ -79,4 +85,14 @@ public String getName() {
return results;
}
+ @Override
+ public Set getItemInfo() {
+ return null;
+ }
+
+ @Override
+ public String getLabel(Locale locale) {
+ return "Test Label";
+ }
+
}
diff --git a/bundles/model/org.eclipse.smarthome.model.persistence/src/org/eclipse/smarthome/model/persistence/extensions/PersistenceExtensions.java b/bundles/model/org.eclipse.smarthome.model.persistence/src/org/eclipse/smarthome/model/persistence/extensions/PersistenceExtensions.java
index b3870863d5a..1735b7c5a42 100644
--- a/bundles/model/org.eclipse.smarthome.model.persistence/src/org/eclipse/smarthome/model/persistence/extensions/PersistenceExtensions.java
+++ b/bundles/model/org.eclipse.smarthome.model.persistence/src/org/eclipse/smarthome/model/persistence/extensions/PersistenceExtensions.java
@@ -53,11 +53,11 @@ public PersistenceExtensions() {
}
public void addPersistenceService(PersistenceService service) {
- services.put(service.getName(), service);
+ services.put(service.getId(), service);
}
public void removePersistenceService(PersistenceService service) {
- services.remove(service.getName());
+ services.remove(service.getId());
}
/**
@@ -718,6 +718,15 @@ private static boolean isDefaultServiceAvailable() {
}
}
+ /**
+ * Get the default persistence service id
+ *
+ * @return {@link String} default service id
+ */
+ public static String getDefaultService() {
+ return defaultService;
+ }
+
@Override
@SuppressWarnings("rawtypes")
public void updated(Dictionary config) throws ConfigurationException {
diff --git a/bundles/model/org.eclipse.smarthome.model.persistence/src/org/eclipse/smarthome/model/persistence/internal/PersistenceManager.java b/bundles/model/org.eclipse.smarthome.model.persistence/src/org/eclipse/smarthome/model/persistence/internal/PersistenceManager.java
index 27aa5d29d7d..94dc376b0ae 100644
--- a/bundles/model/org.eclipse.smarthome.model.persistence/src/org/eclipse/smarthome/model/persistence/internal/PersistenceManager.java
+++ b/bundles/model/org.eclipse.smarthome.model.persistence/src/org/eclipse/smarthome/model/persistence/internal/PersistenceManager.java
@@ -138,15 +138,15 @@ public void unsetItemRegistry(ItemRegistry itemRegistry) {
}
public void addPersistenceService(PersistenceService persistenceService) {
- logger.debug("Initializing {} persistence service.", persistenceService.getName());
- persistenceServices.put(persistenceService.getName(), persistenceService);
- stopEventHandling(persistenceService.getName());
- startEventHandling(persistenceService.getName());
+ logger.debug("Initializing {} persistence service.", persistenceService.getId());
+ persistenceServices.put(persistenceService.getId(), persistenceService);
+ stopEventHandling(persistenceService.getId());
+ startEventHandling(persistenceService.getId());
}
public void removePersistenceService(PersistenceService persistenceService) {
- stopEventHandling(persistenceService.getName());
- persistenceServices.remove(persistenceService.getName());
+ stopEventHandling(persistenceService.getId());
+ persistenceServices.remove(persistenceService.getId());
}
@Override
diff --git a/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/internal/chart/DefaultChartProvider.java b/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/internal/chart/DefaultChartProvider.java
index 85788219cbb..1671cd80968 100644
--- a/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/internal/chart/DefaultChartProvider.java
+++ b/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/internal/chart/DefaultChartProvider.java
@@ -81,12 +81,13 @@ public void unsetItemUIRegistry(ItemUIRegistry itemUIRegistry) {
}
public void addPersistenceService(PersistenceService service) {
- if (service instanceof QueryablePersistenceService)
- persistenceServices.put(service.getName(), (QueryablePersistenceService) service);
+ if (service instanceof QueryablePersistenceService) {
+ persistenceServices.put(service.getId(), (QueryablePersistenceService) service);
+ }
}
public void removePersistenceService(PersistenceService service) {
- persistenceServices.remove(service.getName());
+ persistenceServices.remove(service.getId());
}
static public Map getPersistenceServices() {
@@ -168,8 +169,9 @@ public BufferedImage createChart(String service, String theme, Date startTime, D
String[] itemNames = items.split(",");
for (String itemName : itemNames) {
Item item = itemUIRegistry.getItem(itemName);
- if (addItem(chart, persistenceService, startTime, endTime, item, seriesCounter))
+ if (addItem(chart, persistenceService, startTime, endTime, item, seriesCounter)) {
seriesCounter++;
+ }
}
}
@@ -181,8 +183,9 @@ public BufferedImage createChart(String service, String theme, Date startTime, D
if (item instanceof GroupItem) {
GroupItem groupItem = (GroupItem) item;
for (Item member : groupItem.getMembers()) {
- if (addItem(chart, persistenceService, startTime, endTime, member, seriesCounter))
+ if (addItem(chart, persistenceService, startTime, endTime, member, seriesCounter)) {
seriesCounter++;
+ }
}
} else {
throw new ItemNotFoundException("Item '" + item.getName() + "' defined in groups is not a group.");
@@ -284,8 +287,8 @@ boolean addItem(Chart chart, QueryablePersistenceService service, Date timeBegin
// If the start value is below the median, then count legend position down
// Otherwise count up.
// We use this to decide whether to put the legend in the top or bottom corner.
- if (yData.iterator().next().floatValue() > ((series.getyMax().floatValue() - series.getyMin().floatValue()) / 2 + series
- .getyMin().floatValue())) {
+ if (yData.iterator().next().floatValue() > ((series.getyMax().floatValue() - series.getyMin().floatValue()) / 2
+ + series.getyMin().floatValue())) {
legendPosition++;
} else {
legendPosition--;