/** * Copyright (c) 2010-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.openhab.persistence.jpa.internal; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import javax.persistence.Query; import org.apache.commons.lang.StringUtils; 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.QueryablePersistenceService; import org.openhab.core.types.UnDefType; import org.openhab.persistence.jpa.internal.model.JpaPersistentItem; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * JPA based implementation of QueryablePersistenceService. * * @author Manfred Bergmann * @since 1.6.0 */ public class JpaPersistenceService implements QueryablePersistenceService { private static final Logger logger = LoggerFactory.getLogger(JpaPersistenceService.class); protected ItemRegistry itemRegistry; private EntityManagerFactory emf = null; /** * lazy loading because update() is called after activate() * * @return */ protected EntityManagerFactory getEntityManagerFactory() { if (emf == null) { emf = newEntityManagerFactory(); } return emf; } public void activate() { logger.debug("Activating jpa binding..."); logger.debug("Activating jpa binding...done"); } /** * Closes the EntityPersistenceFactory */ public void deactivate() { logger.debug("Deactivating jpa binding..."); closeEntityManagerFactory(); logger.debug("Deactivating jpa binding...done"); } public void setItemRegistry(ItemRegistry itemRegistry) { this.itemRegistry = itemRegistry; } public void unsetItemRegistry(ItemRegistry itemRegistry) { this.itemRegistry = null; } @Override public String getName() { return "jpa"; } @Override public void store(Item item) { store(item, null); } @Override public void store(Item item, 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!"); logger.error(e.getMessage(), e); em.getTransaction().rollback(); } finally { em.close(); } logger.debug("Storing item...done"); } @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", filter.getBeginDate()); } if (hasEndDate) { query.setParameter("endDate", filter.getEndDate()); } 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); if (historicList != null) { logger.debug(String.format("Convert to HistoricItem: %d", historicList.size())); } em.getTransaction().commit(); return historicList; } catch (Exception e) { logger.error("Error on querying database!"); logger.error(e.getMessage(), 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.debug("Creating EntityManagerFactory..."); Map<String, String> properties = new HashMap<String, String>(); properties.put("javax.persistence.jdbc.url", JpaConfiguration.dbConnectionUrl); properties.put("javax.persistence.jdbc.driver", JpaConfiguration.dbDriverClass); if (JpaConfiguration.dbUserName != null) { properties.put("javax.persistence.jdbc.user", JpaConfiguration.dbUserName); } if (JpaConfiguration.dbPassword != null) { properties.put("javax.persistence.jdbc.password", JpaConfiguration.dbPassword); } if (JpaConfiguration.dbUserName != null && JpaConfiguration.dbPassword == null) { logger.warn("JPA persistence - it is recommended to use a password to protect data store"); } if (JpaConfiguration.dbSyncMapping != null && !StringUtils.isBlank(JpaConfiguration.dbSyncMapping)) { logger.warn("You are settings openjpa.jdbc.SynchronizeMappings, I hope you know what you're doing!"); properties.put("openjpa.jdbc.SynchronizeMappings", JpaConfiguration.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 */ private Item getItemFromRegistry(String itemName) { Item item = null; try { if (itemRegistry != null) { item = itemRegistry.getItem(itemName); } } catch (ItemNotFoundException e1) { logger.error("Unable to get item type for {}", itemName); // Set type to null - data will be returned as StringType item = null; } return item; } }