/** * Copyright (c) 2011, SOCIETIES Consortium (WATERFORD INSTITUTE OF TECHNOLOGY (TSSG), HERIOT-WATT UNIVERSITY (HWU), SOLUTA.NET * (SN), GERMAN AEROSPACE CENTRE (Deutsches Zentrum fuer Luft- und Raumfahrt e.V.) (DLR), Zavod za varnostne tehnologije * informacijske družbe in elektronsko poslovanje (SETCCE), INSTITUTE OF COMMUNICATION AND COMPUTER SYSTEMS (ICCS), LAKE * COMMUNICATIONS (LAKE), INTEL PERFORMANCE LEARNING SOLUTIONS LTD (INTEL), PORTUGAL TELECOM INOVAÇÃO, SA (PTIN), IBM Corp., * INSTITUT TELECOM (ITSUD), AMITEC DIACHYTI EFYIA PLIROFORIKI KAI EPIKINONIES ETERIA PERIORISMENIS EFTHINIS (AMITEC), TELECOM * ITALIA S.p.a.(TI), TRIALOG (TRIALOG), Stiftelsen SINTEF (SINTEF), NEC EUROPE LTD (NEC)) * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following * conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.societies.context.userHistory.impl; import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import org.hibernate.Criteria; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.criterion.Order; import org.hibernate.criterion.Restrictions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.societies.api.context.CtxException; import org.societies.api.context.model.CtxAttribute; import org.societies.api.context.model.CtxAttributeIdentifier; import org.societies.api.context.model.CtxAttributeValueType; import org.societies.api.context.model.CtxHistoryAttribute; import org.societies.api.context.model.CtxIdentifier; import org.societies.api.context.model.CtxModelType; import org.societies.api.context.model.util.SerialisationHelper; import org.societies.context.api.user.db.IUserCtxDBMgr; import org.societies.context.api.user.history.IUserCtxHistoryMgr; import org.societies.context.userHistory.impl.model.UserCtxHistoryAttributeDAO; import org.societies.context.userHistory.impl.model.UserCtxHistoryDAOTranslator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserContextHistoryManagement implements IUserCtxHistoryMgr { /** The logging facility. */ private static final Logger LOG = LoggerFactory.getLogger(UserContextHistoryManagement.class); @Autowired private SessionFactory sessionFactory; @Autowired(required=false) private IUserCtxDBMgr userCtxDBMgr; private boolean ctxRecording = true; public UserContextHistoryManagement() { LOG.info(this.getClass().getName() + " instantiated"); } @Override public CtxHistoryAttribute createHistoryAttribute( final CtxAttribute attribute) throws CtxException{ if (attribute == null) throw new NullPointerException("attribute can't be null"); if (ctxRecording == false) throw new UserCtxHistoryMgrException("context history recording is disabled"); CtxHistoryAttribute result = null; UserCtxHistoryAttributeDAO dao = UserCtxHistoryDAOTranslator.getInstance() .fromCtxAttribute(attribute); final Session session = this.sessionFactory.openSession(); Transaction tx = null; try { tx = session.beginTransaction(); final Serializable historyRecordId = session.save(dao); tx.commit(); dao = (UserCtxHistoryAttributeDAO) session.load( UserCtxHistoryAttributeDAO.class, historyRecordId); result = UserCtxHistoryDAOTranslator.getInstance() .fromUserCtxHistoryAttributeDAO(dao); } catch (Exception e) { tx.rollback(); throw new IllegalStateException("Could not create history for attribute " + attribute + ": " + e.getLocalizedMessage(), e); } finally { if (session != null) session.close(); } return result; } @Override public CtxHistoryAttribute createHistoryAttribute ( final CtxAttributeIdentifier attrId, final Date date, final Serializable value, final CtxAttributeValueType valueType) throws CtxException{ if (attrId == null) throw new NullPointerException("attrId can't be null"); if (date == null) throw new NullPointerException("date can't be null"); if (value == null) throw new NullPointerException("value can't be null"); if (valueType == null) throw new NullPointerException("valueType can't be null"); if (ctxRecording == false) throw new UserCtxHistoryMgrException("context history recording is disabled"); CtxHistoryAttribute result = null; final String stringValue; final Integer integerValue; final Double doubleValue; final byte[] binaryValue; if (valueType.equals(CtxAttributeValueType.STRING)) { stringValue = (String) value; integerValue = null; doubleValue = null; binaryValue = null; } else if (valueType.equals(CtxAttributeValueType.INTEGER)) { stringValue = null; integerValue = (Integer) value; doubleValue = null; binaryValue = null; } else if (valueType.equals(CtxAttributeValueType.DOUBLE)) { stringValue = null; integerValue = null; doubleValue = (Double) value; binaryValue = null; } else if (valueType.equals(CtxAttributeValueType.BINARY)) { stringValue = null; integerValue = null; doubleValue = null; binaryValue = (byte[]) value; } else { stringValue = null; integerValue = null; doubleValue = null; binaryValue = null; } UserCtxHistoryAttributeDAO dao = UserCtxHistoryDAOTranslator.getInstance() .fromCtxAttributeProperties(attrId, date, date, stringValue, integerValue, doubleValue, binaryValue, valueType, null); final Session session = this.sessionFactory.openSession(); Transaction tx = null; try { tx = session.beginTransaction(); final Serializable historyRecordId = session.save(dao); tx.commit(); dao = (UserCtxHistoryAttributeDAO) session.load( UserCtxHistoryAttributeDAO.class, historyRecordId); result = UserCtxHistoryDAOTranslator.getInstance() .fromUserCtxHistoryAttributeDAO(dao); } catch (Exception e) { tx.rollback(); throw new IllegalStateException("Could not create history for attribute " + attrId + ": " + e.getLocalizedMessage(), e); } finally { if (session != null) session.close(); } return result; } @Override public void storeHoCAttribute(CtxAttribute ctxAttribute) throws CtxException{ try { this.createHistoryAttribute(ctxAttribute); List<CtxAttributeIdentifier> escList = new ArrayList<CtxAttributeIdentifier>(); List<CtxAttributeIdentifier> hocTuplesList = this.getCtxHistoryTuples(ctxAttribute.getId(),escList); //System.out.println("hocTuplesList size:"+hocTuplesList.size()); if( hocTuplesList != null && hocTuplesList.size()>0 ) this.storeHoCAttributeTuples(ctxAttribute); } catch (CtxException e) { throw new UserCtxHistoryMgrException("context attribute not stored in context DB" + ctxAttribute.getId() + ": " + e.getLocalizedMessage(), e); } } public void storeHoCAttributeTuples(CtxAttribute ctxAttribute){ //String tupleAttrType = "tuple_"+primaryAttr.getType().toString(); if (LOG.isDebugEnabled()) LOG.debug("storing hoc tuples for " +ctxAttribute.getId()); String tupleAttrType = "tuple_"+ctxAttribute.getId().getType().toString()+"_"+ctxAttribute.getId().getObjectNumber().toString(); if (LOG.isDebugEnabled()) LOG.debug("store: tuple attr type "+ tupleAttrType); // the attr that will maintain the tuples; CtxAttribute tupleAttr = null; List<CtxHistoryAttribute> tupleValueList = new ArrayList<CtxHistoryAttribute>(); try { List<CtxAttributeIdentifier> tempEscListIds = new ArrayList<CtxAttributeIdentifier>(); List<CtxAttributeIdentifier> tupleListIds = this.getCtxHistoryTuples(ctxAttribute.getId(),tempEscListIds); final Set<String> types = new HashSet<String>(); types.add(tupleAttrType); List<CtxIdentifier> tupleAttrIDsList = new ArrayList<CtxIdentifier>(); tupleAttrIDsList.addAll(this.userCtxDBMgr.lookup( ctxAttribute.getOwnerId(), CtxModelType.ATTRIBUTE, types)); if(tupleAttrIDsList.size() > 0){ if (LOG.isDebugEnabled()) LOG.debug("retrieved: "+ tupleAttrType); //tuple_status retrieved CtxAttributeIdentifier ctxAttrId = (CtxAttributeIdentifier) tupleAttrIDsList.get(0); tupleAttr = (CtxAttribute) this.userCtxDBMgr.retrieve(ctxAttrId); } else { if (LOG.isDebugEnabled()) LOG.debug("created: "+ tupleAttrType); //tuple_status created, dead code, the attribute is created by setHocTuples tupleAttr = this.userCtxDBMgr.createAttribute(ctxAttribute.getScope(), tupleAttrType); } //prepare value of ctxAttribute for (CtxAttributeIdentifier tupleAttrID : tupleListIds) { //for one of the escorting attrIds retrieve all history and find the latest value if (LOG.isDebugEnabled()) LOG.debug("Retrieving history for escorting attribute " + tupleAttrID); List<CtxHistoryAttribute> allValues = this.retrieveHistory(tupleAttrID, null, null); //if (LOG.isDebugEnabled()) //LOG.debug("Retrieved history " + allValues); if (allValues != null){ //finding latest hoc value int size = allValues.size(); //if (LOG.isDebugEnabled()) //LOG.debug("Retrieved history size " + size); int last = 0; if (size >= 1){ last = size-1; //if (LOG.isDebugEnabled()) // LOG.debug("Retrieved history last " + last); CtxHistoryAttribute latestHoCAttr2 = allValues.get(last); if (latestHoCAttr2 != null )tupleValueList.add(latestHoCAttr2); } } } byte[] tupleValueListBlob = SerialisationHelper.serialise((Serializable) tupleValueList); if(tupleAttr != null) tupleAttr.setBinaryValue(tupleValueListBlob); //LOG.info("ready to store tupleAttr: "+tupleAttr); CtxHistoryAttribute hocAttr = this.createHistoryAttribute(tupleAttr); } catch (Exception e) { LOG.error("Exception while storing tuples for ctxAttribute id:"+ctxAttribute.getId()+" ."+e.getLocalizedMessage()); e.printStackTrace(); } } @Override public void disableCtxRecording() { ctxRecording = false; } @Override public void enableCtxRecording() { ctxRecording = true; } @Override public List<CtxHistoryAttribute> retrieveHistory(final CtxAttributeIdentifier attrId) throws CtxException{ if (attrId == null) throw new UserCtxHistoryMgrException("attrId can't be null"); final List<CtxHistoryAttribute> results = new LinkedList<CtxHistoryAttribute>(); try { final List<UserCtxHistoryAttributeDAO> historyDAOs = this.retrieve(attrId, null, null); for (final UserCtxHistoryAttributeDAO historyDAO : historyDAOs) results.add(UserCtxHistoryDAOTranslator.getInstance() .fromUserCtxHistoryAttributeDAO(historyDAO)); } catch (Exception e) { throw new UserCtxHistoryMgrException("Could not retrieve history of context attribute " + attrId + ": " + e.getLocalizedMessage(), e); } return results; } @Override public List<CtxHistoryAttribute> retrieveHistory(CtxAttributeIdentifier attrId, final Date startDate, final Date endDate) { final List<CtxHistoryAttribute> results = new LinkedList<CtxHistoryAttribute>(); try { final List<UserCtxHistoryAttributeDAO> historyDAOs = this.retrieve(attrId, startDate, endDate); for (final UserCtxHistoryAttributeDAO historyDAO : historyDAOs) results.add(UserCtxHistoryDAOTranslator.getInstance() .fromUserCtxHistoryAttributeDAO(historyDAO)); } catch (Exception e) { throw new IllegalStateException("Could not retrieve history of context attribute " + attrId + ": " + e.getLocalizedMessage(), e); } return results; } @Override public int removeCtxHistory(CtxAttribute arg0, Date arg1, Date arg2) throws CtxException { return 0; } @Override public int removeHistory(String arg0, Date arg1, Date arg2) throws CtxException { return 0; } @Override public List<CtxHistoryAttribute> retrieveHistory( CtxAttributeIdentifier arg0, int arg1) throws CtxException { return null; } //************************************* // Tuple management //************************************* @Override public List<CtxAttributeIdentifier> getCtxHistoryTuples( CtxAttributeIdentifier primaryAttrIdentifier, List<CtxAttributeIdentifier> arg1) throws CtxException { if(this.userCtxDBMgr == null) return null; List<CtxAttributeIdentifier> tupleAttrIDs = new ArrayList<CtxAttributeIdentifier>(); final String tupleAttrType = "tuple_"+primaryAttrIdentifier.getType().toString()+"_"+primaryAttrIdentifier.getObjectNumber().toString(); List<CtxIdentifier> ls = new ArrayList<CtxIdentifier>(); try { final Set<String> types = new HashSet<String>(); types.add(tupleAttrType); ls.addAll(this.userCtxDBMgr.lookup(primaryAttrIdentifier.getOwnerId(), CtxModelType.ATTRIBUTE, types)); if (ls.size() > 0) { CtxIdentifier id = ls.get(0); final CtxAttribute tupleIdsAttribute = (CtxAttribute) this.userCtxDBMgr.retrieve(id); //deserialise object tupleAttrIDs = (List<CtxAttributeIdentifier>) SerialisationHelper.deserialise(tupleIdsAttribute.getBinaryValue(), this.getClass().getClassLoader()); } } catch (Exception e) { LOG.error("Exception while getting ctx history tuples for id:"+primaryAttrIdentifier+". "+e.getLocalizedMessage()); e.printStackTrace(); } return tupleAttrIDs; } @Override public Boolean removeCtxHistoryTuples(CtxAttributeIdentifier arg0, List<CtxAttributeIdentifier> arg1) throws CtxException { return null; } @Override public Map<CtxHistoryAttribute, List<CtxHistoryAttribute>> retrieveHistoryTuples( CtxAttributeIdentifier arg0, List<CtxAttributeIdentifier> arg1, Date arg2, Date arg3) throws CtxException { return null; } @Override public Boolean setCtxHistoryTuples(CtxAttributeIdentifier primaryAttrIdentifier, List<CtxAttributeIdentifier> listOfEscortingAttributeIds) throws CtxException { boolean result = false; try { // set hoc recording flag for the attributes contained in tuple list final List<CtxAttributeIdentifier> allAttrIds = new ArrayList<CtxAttributeIdentifier>(); // add the primary attr id allAttrIds.add(0,primaryAttrIdentifier); // add the escorting attr ids allAttrIds.addAll(listOfEscortingAttributeIds); // set history flag for all escorting attributes for (CtxAttributeIdentifier escortingAttrID : allAttrIds) { if (escortingAttrID != null ){ CtxAttribute attr = (CtxAttribute) this.userCtxDBMgr.retrieve(escortingAttrID); if(attr != null){ attr.setHistoryRecorded(true); this.userCtxDBMgr.update(attr); this.storeHoCAttribute(attr); } } } //this attr will maintain the attr ids of all the (not only the escorting) hoc_attibutes in a blob final String tupleAttrType = "tuple_"+primaryAttrIdentifier.getType().toString()+"_"+primaryAttrIdentifier.getObjectNumber().toString(); final CtxAttribute tupleAttr = (CtxAttribute) this.userCtxDBMgr.createAttribute(primaryAttrIdentifier.getScope(), tupleAttrType); byte[] attrIdsBlob = SerialisationHelper.serialise((Serializable) allAttrIds); tupleAttr.setBinaryValue(attrIdsBlob); CtxAttribute updatedTupleAttr = (CtxAttribute) this.userCtxDBMgr.update(tupleAttr); if(updatedTupleAttr != null && updatedTupleAttr.getType().contains("tuple_")) result = true; //LOG.info("tuple Attr ids "+allAttrIds); } catch (IOException e) { LOG.error("Exception while setting ctx history tuples for id:"+primaryAttrIdentifier+". "+e.getLocalizedMessage()); e.printStackTrace(); } return result; } @Override public List<CtxAttributeIdentifier> updateCtxHistoryTuples( CtxAttributeIdentifier arg0, List<CtxAttributeIdentifier> arg1) throws CtxException { return null; } //******************** helper methods ***************** @Override public void printHocDB(){ try { final List<UserCtxHistoryAttributeDAO> daos = this.retrieve(null, null, null); System.out.println("key | attr.getId | valueSt | Time "); for (final UserCtxHistoryAttributeDAO dao : daos) { final String valueSt = dao.getStringValue(); final Date date = (Date) dao.getLastUpdated(); System.out.println(dao.getHistoryRecordId()+" | "+dao.getId()+" | "+valueSt+" | "+date.getTime()); } } catch (Exception e) { throw new IllegalStateException("Could not print history of context: " + e.getLocalizedMessage(), e); } } @SuppressWarnings("unchecked") private List<UserCtxHistoryAttributeDAO> retrieve( final CtxAttributeIdentifier ctxId, final Date startDate, final Date endDate) throws Exception { final List<UserCtxHistoryAttributeDAO> result= new LinkedList<UserCtxHistoryAttributeDAO>(); final Session session = sessionFactory.openSession(); try { final Criteria criteria = session.createCriteria(UserCtxHistoryAttributeDAO.class); if (ctxId != null) criteria.add(Restrictions.eq("ctxId", ctxId)); if (startDate != null) criteria.add(Restrictions.ge("lastUpdated", startDate)); if (endDate != null) criteria.add(Restrictions.le("lastUpdated", endDate)); criteria.addOrder(Order.asc("lastUpdated")); result.addAll(criteria.list()); } finally { if (session != null) session.close(); } return result; } }