Codebase as of c53e4aed26 as an initial commit for the shrunk repo
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
32
bundles/org.openhab.persistence.jpa/.classpath
Normal file
32
bundles/org.openhab.persistence.jpa/.classpath
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
<attribute name="test" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
||||
23
bundles/org.openhab.persistence.jpa/.project
Normal file
23
bundles/org.openhab.persistence.jpa/.project
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>org.openhab.persistence.jpa</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
13
bundles/org.openhab.persistence.jpa/NOTICE
Normal file
13
bundles/org.openhab.persistence.jpa/NOTICE
Normal file
@@ -0,0 +1,13 @@
|
||||
This content is produced and maintained by the openHAB project.
|
||||
|
||||
* Project home: https://www.openhab.org
|
||||
|
||||
== Declared Project Licenses
|
||||
|
||||
This program and the accompanying materials are made available under the terms
|
||||
of the Eclipse Public License 2.0 which is available at
|
||||
https://www.eclipse.org/legal/epl-2.0/.
|
||||
|
||||
== Source Code
|
||||
|
||||
https://github.com/openhab/openhab-addons
|
||||
44
bundles/org.openhab.persistence.jpa/README.md
Normal file
44
bundles/org.openhab.persistence.jpa/README.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# Java Persistence API (JPA) Persistence
|
||||
|
||||
This service allows you to persist state updates using a SQL or NoSQL database through the [Java Persistence API](https://en.wikipedia.org/wiki/Java_Persistence_API).
|
||||
The service uses an abstraction layer that theoretically allows it to support many available SQL or NoSQL databases.
|
||||
|
||||
It will create one table named `historic_item` where all item states are stored.
|
||||
The item state is stored in a string representation.
|
||||
|
||||
The service currently supports MySQL, Apache Derby and PostgreSQL databases.
|
||||
Only the embedded Apache Derby database driver is included.
|
||||
Other drivers must be installed manually.
|
||||
(See below for more information on that.)
|
||||
|
||||
## Configuration
|
||||
|
||||
This service can be configured in the file `services/jpa.cfg`.
|
||||
|
||||
| Property | Default | Required | Description |
|
||||
| -------- | ------- | :-------: | ------------------------------------------------------------ |
|
||||
| url | | Yes | JDBC connection URL. Examples:<br/><br/>`jdbc:postgresql://hab.local:5432/openhab`<br/>`jdbc:derby://hab.local:1527/openhab;create=true`<br/>`jdbc:mysql://localhost:3306/openhab` |
|
||||
| driver | | Yes | database driver. Examples:<br/><br/>`org.postgresql.Driver`<br/>`org.apache.derby.jdbc.ClientDriver`<br/>`com.mysql.jdbc.Driver`<br/></br>Only the Apache Derby driver is included with the service. Drivers for other databases must be installed manually. This is a trivial process. Normally JDBC database drivers are packaged as OSGi bundles and can just be dropped into the `addons` folder. This has the advantage that users can update their drivers as needed. The following database drivers are known to work:<br/><br/>`postgresql-9.4-1203-jdbc41.jar`<br/>`postgresql-9.4-1206-jdbc41.jar` |
|
||||
| user | | if needed | database user name for connection |
|
||||
| password | | if needed | database user password for connection |
|
||||
|
||||
## Adding support for other JPA supported databases
|
||||
|
||||
All item- and event-related configuration is done in the file `persistence/jpa.persist`.
|
||||
|
||||
If a database driver is not an OSGi bundle, the technique below can be used to extend the openHAB classpath.
|
||||
|
||||
Other database drivers can be added by expanding the openHAB classpath.
|
||||
|
||||
Use the following classpath setup in start.sh / start_debug.sh of openHAB:
|
||||
|
||||
```
|
||||
cp=$(echo lib/*.jar | tr ' ' ':'):$(find $eclipsehome -name "org.eclipse.equinox.launcher_*.jar" | sort | tail -1);
|
||||
```
|
||||
|
||||
This will add all .jar files in a folder "lib" in the root of openhab.
|
||||
|
||||
All databases that are supported by JPA can be added.
|
||||
|
||||
Define `driver` and `url` according to the database definitions.
|
||||
|
||||
70
bundles/org.openhab.persistence.jpa/pom.xml
Normal file
70
bundles/org.openhab.persistence.jpa/pom.xml
Normal file
@@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
|
||||
<version>3.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.openhab.persistence.jpa</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: Persistence Service :: JPA</name>
|
||||
|
||||
<properties>
|
||||
<bnd.importpackage>!com.ibm.*,!com.sun.*,!oracle.*,!org.apache.bval.*,!org.apache.geronimo.*,!org.apache.avalon.*,!org.apache.log,!org.apache.tools.*,!org.apache.xerces.*,!org.jboss.*,!org.postgresql.*,!org.slf4j.impl,!weblogic.*,!javax.rmi</bnd.importpackage>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- https://mvnrepository.com/artifact/org.apache.openjpa/openjpa-all -->
|
||||
<dependency>
|
||||
<groupId>org.apache.openjpa</groupId>
|
||||
<artifactId>openjpa-all</artifactId>
|
||||
<version>2.4.0</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/org.apache.derby/derby -->
|
||||
<dependency>
|
||||
<groupId>org.apache.derby</groupId>
|
||||
<artifactId>derby</artifactId>
|
||||
<version>10.11.1.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.openjpa</groupId>
|
||||
<artifactId>openjpa-maven-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<configuration>
|
||||
<excludes>org/apache/bval/**</excludes>
|
||||
<includes>**/model/*.class</includes>
|
||||
<addDefaultConstructor>true</addDefaultConstructor>
|
||||
<enforcePropertyRestrictions>true</enforcePropertyRestrictions>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.openjpa</groupId>
|
||||
<artifactId>openjpa</artifactId>
|
||||
<!-- set the version to be the same as the level in your runtime -->
|
||||
<version>3.1.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>enhancer</id>
|
||||
<goals>
|
||||
<goal>enhance</goal>
|
||||
</goals>
|
||||
<phase>process-classes</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.persistence.jpa-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
|
||||
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
|
||||
|
||||
<feature name="openhab-persistence-jpa" description="JPA Persistence" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.persistence.jpa/${project.version}</bundle>
|
||||
<configfile finalname="${openhab.conf}/services/jpa.cfg" override="false">mvn:${project.groupId}/openhab-addons-external3/${project.version}/cfg/jpa</configfile>
|
||||
</feature>
|
||||
|
||||
</features>
|
||||
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.persistence.jpa.internal;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The configuration required for Jpa binding.
|
||||
*
|
||||
* @author Manfred Bergmann - Initial contribution
|
||||
* @author Kai Kreuzer - migrated to 3.x
|
||||
*
|
||||
*/
|
||||
public class JpaConfiguration {
|
||||
private final Logger logger = LoggerFactory.getLogger(JpaConfiguration.class);
|
||||
|
||||
private static final String CFG_CONNECTION_URL = "url";
|
||||
private static final String CFG_DRIVER_CLASS = "driver";
|
||||
private static final String CFG_USERNAME = "user";
|
||||
private static final String CFG_PASSWORD = "password";
|
||||
private static final String CFG_SYNCMAPPING = "syncmappings";
|
||||
|
||||
public static boolean isInitialized = false;
|
||||
|
||||
public final String dbConnectionUrl;
|
||||
public final String dbDriverClass;
|
||||
public final String dbUserName;
|
||||
public final String dbPassword;
|
||||
public final String dbSyncMapping;
|
||||
|
||||
public JpaConfiguration(final Map<String, Object> properties) {
|
||||
logger.debug("Update config...");
|
||||
|
||||
String param = (String) properties.get(CFG_CONNECTION_URL);
|
||||
logger.debug("url: {}", param);
|
||||
if (param == null) {
|
||||
logger.warn("Connection url is required in jpa.cfg!");
|
||||
} else if (param.isBlank()) {
|
||||
logger.warn("Empty connection url in jpa.cfg!");
|
||||
}
|
||||
dbConnectionUrl = param;
|
||||
|
||||
param = (String) properties.get(CFG_DRIVER_CLASS);
|
||||
logger.debug("driver: {}", param);
|
||||
if (param == null) {
|
||||
logger.warn("Driver class is required in jpa.cfg!");
|
||||
} else if (param.isBlank()) {
|
||||
logger.warn("Empty driver class in jpa.cfg!");
|
||||
}
|
||||
dbDriverClass = param;
|
||||
|
||||
if (properties.get(CFG_USERNAME) == null) {
|
||||
logger.info("{} was not specified!", CFG_USERNAME);
|
||||
}
|
||||
dbUserName = (String) properties.get(CFG_USERNAME);
|
||||
|
||||
if (properties.get(CFG_PASSWORD) == null) {
|
||||
logger.info("{} was not specified!", CFG_PASSWORD);
|
||||
}
|
||||
dbPassword = (String) properties.get(CFG_PASSWORD);
|
||||
|
||||
if (properties.get(CFG_SYNCMAPPING) == null) {
|
||||
logger.debug("{} was not specified!", CFG_SYNCMAPPING);
|
||||
}
|
||||
dbSyncMapping = (String) properties.get(CFG_SYNCMAPPING);
|
||||
|
||||
isInitialized = true;
|
||||
logger.debug("Update config... done");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.persistence.jpa.internal;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.openhab.core.items.Item;
|
||||
import org.openhab.core.library.items.ContactItem;
|
||||
import org.openhab.core.library.items.DateTimeItem;
|
||||
import org.openhab.core.library.items.DimmerItem;
|
||||
import org.openhab.core.library.items.LocationItem;
|
||||
import org.openhab.core.library.items.NumberItem;
|
||||
import org.openhab.core.library.items.RollershutterItem;
|
||||
import org.openhab.core.library.items.SwitchItem;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.OpenClosedType;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.library.types.PointType;
|
||||
import org.openhab.core.library.types.StringListType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.persistence.HistoricItem;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.persistence.jpa.internal.model.JpaPersistentItem;
|
||||
|
||||
/**
|
||||
* The historic item as returned when querying the service.
|
||||
*
|
||||
* @author Manfred Bergmann - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class JpaHistoricItem implements HistoricItem {
|
||||
|
||||
private final String name;
|
||||
private final State state;
|
||||
private final ZonedDateTime timestamp;
|
||||
|
||||
public JpaHistoricItem(String name, State state, ZonedDateTime timestamp) {
|
||||
this.name = name;
|
||||
this.state = state;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ZonedDateTime getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public State getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return DateFormat.getDateTimeInstance().format(timestamp) + ": " + name + " -> " + state.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method maps a jpa result item to this historic item.
|
||||
*
|
||||
* @param jpaQueryResult the result which jpa items
|
||||
* @param item used for query information, like the state (State)
|
||||
* @return list of historic items
|
||||
*/
|
||||
public static List<HistoricItem> fromResultList(List<JpaPersistentItem> jpaQueryResult, Item item) {
|
||||
List<HistoricItem> ret = new ArrayList<>();
|
||||
for (JpaPersistentItem i : jpaQueryResult) {
|
||||
HistoricItem hi = fromPersistedItem(i, item);
|
||||
ret.add(hi);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the string value of the persisted item to the state of a HistoricItem.
|
||||
*
|
||||
* @param pItem the persisted JpaPersistentItem
|
||||
* @param item the source reference Item
|
||||
* @return historic item
|
||||
*/
|
||||
public static HistoricItem fromPersistedItem(JpaPersistentItem pItem, Item item) {
|
||||
State state;
|
||||
if (item instanceof NumberItem) {
|
||||
state = new DecimalType(Double.valueOf(pItem.getValue()));
|
||||
} else if (item instanceof DimmerItem) {
|
||||
state = new PercentType(Integer.valueOf(pItem.getValue()));
|
||||
} else if (item instanceof SwitchItem) {
|
||||
state = OnOffType.valueOf(pItem.getValue());
|
||||
} else if (item instanceof ContactItem) {
|
||||
state = OpenClosedType.valueOf(pItem.getValue());
|
||||
} else if (item instanceof RollershutterItem) {
|
||||
state = PercentType.valueOf(pItem.getValue());
|
||||
} else if (item instanceof DateTimeItem) {
|
||||
state = new DateTimeType(ZonedDateTime.ofInstant(Instant.ofEpochMilli(Long.valueOf(pItem.getValue())),
|
||||
ZoneId.systemDefault()));
|
||||
} else if (item instanceof LocationItem) {
|
||||
PointType pType = null;
|
||||
String[] comps = pItem.getValue().split(";");
|
||||
if (comps.length >= 2) {
|
||||
pType = new PointType(new DecimalType(comps[0]), new DecimalType(comps[1]));
|
||||
|
||||
if (comps.length == 3) {
|
||||
pType.setAltitude(new DecimalType(comps[2]));
|
||||
}
|
||||
}
|
||||
state = pType;
|
||||
} else if (item instanceof StringListType) {
|
||||
state = new StringListType(pItem.getValue());
|
||||
} else {
|
||||
state = new StringType(pItem.getValue());
|
||||
}
|
||||
|
||||
return new JpaHistoricItem(item.getName(), state, pItem.getTimestamp());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,322 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.persistence.jpa.internal;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.persistence.Persistence;
|
||||
import javax.persistence.Query;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.items.Item;
|
||||
import org.openhab.core.items.ItemNotFoundException;
|
||||
import org.openhab.core.items.ItemRegistry;
|
||||
import org.openhab.core.persistence.FilterCriteria;
|
||||
import org.openhab.core.persistence.FilterCriteria.Ordering;
|
||||
import org.openhab.core.persistence.HistoricItem;
|
||||
import org.openhab.core.persistence.PersistenceItemInfo;
|
||||
import org.openhab.core.persistence.PersistenceService;
|
||||
import org.openhab.core.persistence.QueryablePersistenceService;
|
||||
import org.openhab.core.persistence.strategy.PersistenceStrategy;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.openhab.persistence.jpa.internal.model.JpaPersistentItem;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.ConfigurationPolicy;
|
||||
import org.osgi.service.component.annotations.Deactivate;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* JPA based implementation of QueryablePersistenceService.
|
||||
*
|
||||
* @author Manfred Bergmann - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(service = { PersistenceService.class,
|
||||
QueryablePersistenceService.class }, configurationPid = "org.openhab.jpa", configurationPolicy = ConfigurationPolicy.REQUIRE)
|
||||
public class JpaPersistenceService implements QueryablePersistenceService {
|
||||
private final Logger logger = LoggerFactory.getLogger(JpaPersistenceService.class);
|
||||
|
||||
private final ItemRegistry itemRegistry;
|
||||
|
||||
private @Nullable EntityManagerFactory emf = null;
|
||||
|
||||
private @NonNullByDefault({}) JpaConfiguration config;
|
||||
|
||||
@Activate
|
||||
public JpaPersistenceService(final @Reference ItemRegistry itemRegistry) {
|
||||
this.itemRegistry = itemRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* lazy loading because update() is called after activate()
|
||||
*
|
||||
* @return EntityManagerFactory
|
||||
*/
|
||||
protected @Nullable EntityManagerFactory getEntityManagerFactory() {
|
||||
if (emf == null) {
|
||||
emf = newEntityManagerFactory();
|
||||
}
|
||||
return emf;
|
||||
}
|
||||
|
||||
@Activate
|
||||
public void activate(BundleContext context, Map<String, Object> properties) {
|
||||
logger.debug("Activating jpa persistence service");
|
||||
config = new JpaConfiguration(properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the EntityPersistenceFactory
|
||||
*/
|
||||
@Deactivate
|
||||
public void deactivate() {
|
||||
logger.debug("Deactivating jpa persistence service");
|
||||
closeEntityManagerFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "jpa";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel(@Nullable Locale locale) {
|
||||
return "JPA";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void store(Item item) {
|
||||
store(item, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void store(Item item, @Nullable String alias) {
|
||||
logger.debug("Storing item: {}", item.getName());
|
||||
|
||||
if (item.getState() instanceof UnDefType) {
|
||||
logger.debug("This item is of undefined type. Cannot persist it!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!JpaConfiguration.isInitialized) {
|
||||
logger.debug("Trying to create EntityManagerFactory but we don't have configuration yet!");
|
||||
return;
|
||||
}
|
||||
|
||||
// determine item name to be stored
|
||||
String name = (alias != null) ? alias : item.getName();
|
||||
|
||||
JpaPersistentItem pItem = new JpaPersistentItem();
|
||||
try {
|
||||
String newValue = StateHelper.toString(item.getState());
|
||||
pItem.setValue(newValue);
|
||||
logger.debug("Stored new value: {}", newValue);
|
||||
} catch (Exception e1) {
|
||||
logger.error("Error on converting state value to string: {}", e1.getMessage());
|
||||
return;
|
||||
}
|
||||
pItem.setName(name);
|
||||
pItem.setRealName(item.getName());
|
||||
pItem.setTimestamp(new Date());
|
||||
|
||||
EntityManager em = getEntityManagerFactory().createEntityManager();
|
||||
try {
|
||||
logger.debug("Persisting item...");
|
||||
// In RESOURCE_LOCAL calls to EntityManager require a begin/commit
|
||||
em.getTransaction().begin();
|
||||
em.persist(pItem);
|
||||
em.getTransaction().commit();
|
||||
logger.debug("Persisting item...done");
|
||||
} catch (Exception e) {
|
||||
logger.error("Error on persisting item! Rolling back!", e);
|
||||
em.getTransaction().rollback();
|
||||
} finally {
|
||||
em.close();
|
||||
}
|
||||
|
||||
logger.debug("Storing item...done");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<PersistenceItemInfo> getItemInfo() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<HistoricItem> query(FilterCriteria filter) {
|
||||
logger.debug("Querying for historic item: {}", filter.getItemName());
|
||||
|
||||
if (!JpaConfiguration.isInitialized) {
|
||||
logger.warn("Trying to create EntityManagerFactory but we don't have configuration yet!");
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
String itemName = filter.getItemName();
|
||||
Item item = getItemFromRegistry(itemName);
|
||||
|
||||
String sortOrder;
|
||||
if (filter.getOrdering() == Ordering.ASCENDING) {
|
||||
sortOrder = "ASC";
|
||||
} else {
|
||||
sortOrder = "DESC";
|
||||
}
|
||||
|
||||
boolean hasBeginDate = false;
|
||||
boolean hasEndDate = false;
|
||||
String queryString = "SELECT n FROM " + JpaPersistentItem.class.getSimpleName()
|
||||
+ " n WHERE n.realName = :itemName";
|
||||
if (filter.getBeginDate() != null) {
|
||||
queryString += " AND n.timestamp >= :beginDate";
|
||||
hasBeginDate = true;
|
||||
}
|
||||
if (filter.getEndDate() != null) {
|
||||
queryString += " AND n.timestamp <= :endDate";
|
||||
hasEndDate = true;
|
||||
}
|
||||
queryString += " ORDER BY n.timestamp " + sortOrder;
|
||||
|
||||
logger.debug("The query: {}", queryString);
|
||||
|
||||
EntityManager em = getEntityManagerFactory().createEntityManager();
|
||||
try {
|
||||
// In RESOURCE_LOCAL calls to EntityManager require a begin/commit
|
||||
em.getTransaction().begin();
|
||||
|
||||
logger.debug("Creating query...");
|
||||
Query query = em.createQuery(queryString);
|
||||
query.setParameter("itemName", item.getName());
|
||||
if (hasBeginDate) {
|
||||
query.setParameter("beginDate", Date.from(filter.getBeginDate().toInstant()));
|
||||
}
|
||||
if (hasEndDate) {
|
||||
query.setParameter("endDate", Date.from(filter.getEndDate().toInstant()));
|
||||
}
|
||||
|
||||
query.setFirstResult(filter.getPageNumber() * filter.getPageSize());
|
||||
query.setMaxResults(filter.getPageSize());
|
||||
logger.debug("Creating query...done");
|
||||
|
||||
logger.debug("Retrieving result list...");
|
||||
@SuppressWarnings("unchecked")
|
||||
List<JpaPersistentItem> result = query.getResultList();
|
||||
logger.debug("Retrieving result list...done");
|
||||
|
||||
List<HistoricItem> historicList = JpaHistoricItem.fromResultList(result, item);
|
||||
logger.debug("{}", String.format("Convert to HistoricItem: %d", historicList.size()));
|
||||
|
||||
em.getTransaction().commit();
|
||||
|
||||
return historicList;
|
||||
} catch (Exception e) {
|
||||
logger.error("Error on querying database!", e);
|
||||
em.getTransaction().rollback();
|
||||
} finally {
|
||||
em.close();
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new EntityManagerFactory with properties read from openhab.cfg via JpaConfiguration.
|
||||
*
|
||||
* @return initialized EntityManagerFactory
|
||||
*/
|
||||
protected EntityManagerFactory newEntityManagerFactory() {
|
||||
logger.trace("Creating EntityManagerFactory...");
|
||||
|
||||
Map<String, String> properties = new HashMap<>();
|
||||
properties.put("javax.persistence.jdbc.url", config.dbConnectionUrl);
|
||||
properties.put("javax.persistence.jdbc.driver", config.dbDriverClass);
|
||||
if (config.dbUserName != null) {
|
||||
properties.put("javax.persistence.jdbc.user", config.dbUserName);
|
||||
}
|
||||
if (config.dbPassword != null) {
|
||||
properties.put("javax.persistence.jdbc.password", config.dbPassword);
|
||||
}
|
||||
if (config.dbUserName != null && config.dbPassword == null) {
|
||||
logger.warn("JPA persistence - it is recommended to use a password to protect data store");
|
||||
}
|
||||
if (config.dbSyncMapping != null && !config.dbSyncMapping.isBlank()) {
|
||||
logger.warn("You are settings openjpa.jdbc.SynchronizeMappings, I hope you know what you're doing!");
|
||||
properties.put("openjpa.jdbc.SynchronizeMappings", config.dbSyncMapping);
|
||||
}
|
||||
|
||||
EntityManagerFactory fac = Persistence.createEntityManagerFactory(getPersistenceUnitName(), properties);
|
||||
logger.debug("Creating EntityManagerFactory...done");
|
||||
|
||||
return fac;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes EntityManagerFactory
|
||||
*/
|
||||
protected void closeEntityManagerFactory() {
|
||||
if (emf != null) {
|
||||
emf.close();
|
||||
emf = null;
|
||||
}
|
||||
logger.debug("Closing down entity objects...done");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if EntityManagerFactory is open
|
||||
*
|
||||
* @return true when open, false otherwise
|
||||
*/
|
||||
protected boolean isEntityManagerFactoryOpen() {
|
||||
return emf != null && emf.isOpen();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the persistence unit as in persistence.xml file.
|
||||
*
|
||||
* @return the persistence unit name
|
||||
*/
|
||||
protected String getPersistenceUnitName() {
|
||||
return "default";
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the item for the given name from the item registry
|
||||
*
|
||||
* @param itemName
|
||||
* @return item
|
||||
*/
|
||||
private @Nullable Item getItemFromRegistry(String itemName) {
|
||||
try {
|
||||
return itemRegistry.getItem(itemName);
|
||||
} catch (ItemNotFoundException e1) {
|
||||
logger.error("Unable to get item type for {}", itemName);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PersistenceStrategy> getDefaultStrategies() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.persistence.jpa.internal;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.PointType;
|
||||
import org.openhab.core.types.State;
|
||||
|
||||
/**
|
||||
* Helper class for dealing with State
|
||||
*
|
||||
* @author Manfred Bergmann - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class StateHelper {
|
||||
|
||||
/**
|
||||
* Converts the given State to a string that can be persisted in db
|
||||
*
|
||||
* @param state the state of the item to be persisted
|
||||
* @return state converted as string
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String toString(State state) throws Exception {
|
||||
if (state instanceof DateTimeType) {
|
||||
return String.valueOf(((DateTimeType) state).getZonedDateTime().toInstant().toEpochMilli());
|
||||
}
|
||||
if (state instanceof DecimalType) {
|
||||
return String.valueOf(((DecimalType) state).doubleValue());
|
||||
}
|
||||
if (state instanceof PointType) {
|
||||
PointType pType = (PointType) state;
|
||||
return String.format(Locale.ENGLISH, "%f;%f;%f", pType.getLatitude().doubleValue(),
|
||||
pType.getLongitude().doubleValue(), pType.getAltitude().doubleValue());
|
||||
}
|
||||
|
||||
return state.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.persistence.jpa.internal.model;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
|
||||
import org.openhab.core.persistence.HistoricItem;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
|
||||
/**
|
||||
* This is the DAO object used for storing and retrieving to and from database.
|
||||
*
|
||||
* @author Manfred Bergmann - Initial contribution
|
||||
*
|
||||
*/
|
||||
|
||||
@Entity
|
||||
@Table(name = "HISTORIC_ITEM")
|
||||
public class JpaPersistentItem implements HistoricItem {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
private Long id;
|
||||
|
||||
private String name = "";
|
||||
private String realName = "";
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date timestamp = new Date();
|
||||
@Column(length = 32672) // 32k, max varchar for apache derby
|
||||
private String value = "";
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getRealName() {
|
||||
return realName;
|
||||
}
|
||||
|
||||
public void setRealName(String realName) {
|
||||
this.realName = realName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ZonedDateTime getTimestamp() {
|
||||
return ZonedDateTime.ofInstant(timestamp.toInstant(), ZoneId.systemDefault());
|
||||
}
|
||||
|
||||
public void setTimestamp(Date timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public State getState() {
|
||||
return UnDefType.NULL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return DateFormat.getDateTimeInstance().format(getTimestamp()) + ": " + getName() + " -> " + value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
|
||||
|
||||
<persistence-unit name="default" transaction-type="RESOURCE_LOCAL">
|
||||
<provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
|
||||
<non-jta-data-source/>
|
||||
<class>org.openhab.persistence.jpa.internal.model.JpaPersistentItem</class>
|
||||
<exclude-unlisted-classes>true</exclude-unlisted-classes>
|
||||
<properties>
|
||||
<property name="javax.persistence.jdbc.url" value="jdbc:postgresql:"/>
|
||||
<property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver"/>
|
||||
<property name="javax.persistence.jdbc.user" value=""/>
|
||||
<property name="javax.persistence.jdbc.password" value=""/>
|
||||
<property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(schemaAction='add')"/>
|
||||
<property name="openjpa.Log" value="DefaultLevel=WARN, Tool=INFO"/>
|
||||
</properties>
|
||||
</persistence-unit>
|
||||
|
||||
<persistence-unit name="default_test" transaction-type="RESOURCE_LOCAL">
|
||||
<provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
|
||||
<non-jta-data-source/>
|
||||
<class>org.openhab.persistence.jpa.internal.model.JpaPersistentItem</class>
|
||||
<exclude-unlisted-classes>true</exclude-unlisted-classes>
|
||||
<properties>
|
||||
<property name="javax.persistence.jdbc.url" value="jdbc:derby:data;create=true"/>
|
||||
<property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.EmbeddedDriver"/>
|
||||
<property name="javax.persistence.jdbc.user" value="APP"/>
|
||||
<property name="javax.persistence.jdbc.password" value="APP"/>
|
||||
<property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(SchemaAction='drop,add')"/>
|
||||
<property name="openjpa.Log" value="DefaultLevel=TRACE, Tool=INFO"/>
|
||||
</properties>
|
||||
</persistence-unit>
|
||||
|
||||
</persistence>
|
||||
Reference in New Issue
Block a user