/******************************************************************************* * Copyright 2014 Federal Chancellery Austria * MOA-ID has been developed in a cooperation between BRZ, the Federal * Chancellery Austria - ICT staff unit, and Graz University of Technology. * * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by * the European Commission - subsequent versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: * http://www.osor.eu/eupl/ * * Unless required by applicable law or agreed to in writing, software * distributed under the Licence is distributed on an "AS IS" basis, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and * limitations under the Licence. * * This product combines work with different licenses. See the "NOTICE" text * file for details on the various modules and licenses. * The "NOTICE" text file is part of the distribution. Any derivative works * that you distribute must include a readable copy of the "NOTICE" text file. *******************************************************************************/ package at.gv.egovernment.moa.id.storage; import java.io.Serializable; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Random; import java.util.concurrent.TimeUnit; import org.apache.commons.lang.SerializationUtils; import org.hibernate.HibernateException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.data.redis.core.RedisOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.SessionCallback; import org.springframework.data.redis.serializer.JacksonJsonRedisSerializer; import org.springframework.stereotype.Service; import at.gv.egiz.eaaf.core.api.storage.ITransactionStorage; import at.gv.egiz.eaaf.core.exceptions.EAAFException; import at.gv.egovernment.moa.id.auth.exception.AuthenticationException; import at.gv.egovernment.moa.id.commons.api.AuthConfiguration; import at.gv.egovernment.moa.id.commons.db.dao.session.AssertionStore; import at.gv.egovernment.moa.id.commons.db.ex.MOADatabaseException; import at.gv.egovernment.moa.logging.Logger; import at.gv.egovernment.moa.util.MiscUtil; @Service("TransactionStorage") public class RedisTransactionStorage implements ITransactionStorage { @Autowired private RedisTemplate redisTemplate; @Autowired protected AuthConfiguration authConfig; @Autowired private JacksonJsonRedisSerializer assertionStoreSerializer; public RedisTemplate getTemplate(){ return this.redisTemplate; } public void setTemplate(RedisTemplate t){ this.redisTemplate = t; } public boolean containsKey(String key) { try { searchInDatabase(key); return true; } catch (MOADatabaseException e) { return false; } } /* (non-Javadoc) * @see at.gv.egovernment.moa.id.storage.ITransactionStorage#changeKey(java.lang.String, java.lang.String, java.lang.Object) */ @Override public void changeKey(String oldKey, String newKey, Object value) throws MOADatabaseException { //search if key already exists final int expTime = redisTemplate.getExpire(oldKey, TimeUnit.MILLISECONDS).intValue(); //AssertionStore element = searchInDatabase(oldKey); if (expTime < 0) { Logger.info("No transaction-data with oldKey:" + oldKey + " found. Process gets stopped."); throw new MOADatabaseException("No transaction-data with oldKey:" + oldKey + " found. Process gets stopped."); } //Important: Rename not working here, because the new ID also has to be put into the //value object. //redisTemplate.rename(oldKey, newKey); final String old_key = oldKey; //redisTemplate.delete(oldKey); //put(null, newKey, value, expTime); final AssertionStore assertion = prepareAssertion(null, newKey, value); List txResults = redisTemplate.execute(new SessionCallback>() { public List execute(RedisOperations operations) throws DataAccessException { operations.multi(); operations.delete(old_key); operations.opsForValue().set(assertion.getArtifact(), new String(assertionStoreSerializer.serialize(assertion)),expTime,TimeUnit.MILLISECONDS); // This will contain the results of all ops in the transaction return operations.exec(); } }); int a= txResults.size(); } public void put(String key, Object value, int timeoutms) throws MOADatabaseException { //search if key already exists AssertionStore element = searchInDatabase(key); //create a new entry if key does not exists already if (element == null) { element = new AssertionStore(); } put(element, key, value, timeoutms); } public T get(String key, final Class clazz) throws MOADatabaseException { try { return get(key, clazz, -1); } catch (AuthenticationException e) { //this execption only occurs if an additional timeOut is used Logger.error("This exeption should not occur!!!!", e); return null; } } public Object get(String key) throws MOADatabaseException { AssertionStore element = searchInDatabase(key); if (element == null) return null; return SerializationUtils.deserialize(element.getAssertion()); } public T get(String key, final Class clazz, long dataTimeOut) throws MOADatabaseException, AuthenticationException { AssertionStore element = searchInDatabase(key); if (element == null) return null; // dataTimeOut = -1; // if (dataTimeOut > -1) { // //check timeout // long now = new Date().getTime(); // // if (now - element.getDatatime().getTime() > dataTimeOut) { // Logger.info("Transaction-Data with key: " + key + " is out of time."); // throw new AuthenticationException("1207", new Object[] { key }); // // } // } //Deserialize Assertion Object data = SerializationUtils.deserialize(element.getAssertion()); //check if assertion has the correct class type try { @SuppressWarnings("unchecked") T test = (T) Class.forName(element.getType()).cast(data); return test; } catch (Exception e) { Logger.warn("Sessioninformation Cast-Exception by using Artifact=" + key); throw new MOADatabaseException("Sessioninformation Cast-Exception"); } } //NOT USED with REDIS public List clean(Date now, long dataTimeOut) { //redis enables to set TTL when creating new values, so we don't need this function anymore // Date expioredate = new Date(now.getTime() - dataTimeOut); // // List results; List returnValues = new ArrayList(); // Session session = MOASessionDBUtils.getCurrentSession(); // // synchronized (session) { // session.beginTransaction(); // Query query = session.getNamedQuery("getAssertionWithTimeOut"); // query.setTimestamp("timeout", expioredate); // results = query.list(); // session.getTransaction().commit(); // } // // if (results != null) { // for (AssertionStore el : results) // returnValues.add(el.getArtifact()); // // } return returnValues; } public void remove(String key) { try { AssertionStore element = searchInDatabase(key); if (element == null) { Logger.debug("Sessioninformation not removed! (Sessioninformation with ID=" + key + "not found)"); return; } redisTemplate.delete(key); //cleanDelete(element); Logger.debug("Removed stored information with ID: " + key); } catch (MOADatabaseException e) { Logger.info("Sessioninformation not removed! (Message:"+ e.getMessage() + ")"); } catch (HibernateException e) { Logger.warn("Sessioninformation not removed! (Error during Database communication)", e); } } //Not used within REDIS store private void cleanDelete(AssertionStore element) { // try { // element.setAssertion("blank".getBytes()); // MOASessionDBUtils.saveOrUpdate(element); // // } catch (MOADatabaseException e) { // Logger.warn("Blank shortTime session with artifact=" + element.getArtifact() + " FAILED.", e); // // } finally { // if (!MOASessionDBUtils.delete(element)) // Logger.error("ShortTime session with artifact=" + element.getArtifact() // + " not removed! (Error during Database communication)"); // // } } //name="getAssertionWithArtifact", query = "select assertionstore from AssertionStore assertionstore where assertionstore.artifact = :artifact"), //@NamedQuery(name="getAssertionWithTimeOut", query = "select assertionstore from AssertionStore assertionstore where assertionstore.timestamp < :timeout") @SuppressWarnings("rawtypes") private AssertionStore searchInDatabase(String artifact) throws MOADatabaseException { MiscUtil.assertNotNull(artifact, "artifact"); Logger.trace("Getting sessioninformation with ID " + artifact + " from database."); // Session session = MOASessionDBUtils.getCurrentSession(); // List result; // // synchronized (session) { // session.beginTransaction(); // Query query = session.getNamedQuery("getAssertionWithArtifact"); // query.setParameter("artifact", artifact); // result = query.list(); // // //send transaction // session.getTransaction().commit(); // } //String id = (String) redisTemplate.opsForSet().pop(artifact); String assertion = (String) redisTemplate.opsForValue().get(artifact); //String id = (String) redisTemplate.opsForValue().get(artifact); if(assertion == null){ Logger.debug("No transaction information with ID:" + artifact + " found."); return null; } AssertionStore as = (AssertionStore) assertionStoreSerializer.deserialize(assertion.getBytes()); //delete the timestamp entry // String ts = as.getDatatime().toString(); // redisTemplate.opsForSet().pop(ts); if(as == null){ Logger.debug("No transaction information with ID:" + artifact + " found."); return null; } return as; //Assertion requires an unique artifact // if (result.size() != 1) { // Logger.debug("No transaction information with ID:" + artifact + " found."); // // // } // // return (AssertionStore) result.get(0); } private void put(AssertionStore element, String key, Object value, int timeoutms) throws MOADatabaseException { element = prepareAssertion(element, key, value); int authDataTimeOut = authConfig.getTransactionTimeOut() * 1000; if(timeoutms != -1){ authDataTimeOut = timeoutms; } redisTemplate.opsForValue().set(element.getArtifact(), new String(assertionStoreSerializer.serialize(element)),authDataTimeOut,TimeUnit.MILLISECONDS); //MOASessionDBUtils.saveOrUpdate(element); Logger.debug(value.getClass().getName() + " with ID: " + key + " is stored in Database"); } private AssertionStore prepareAssertion(AssertionStore element, String key, Object value) throws MOADatabaseException { if(element == null) element = new AssertionStore(); element.setArtifact(key); element.setType(value.getClass().getName()); element.setDatatime(new Date()); if (!Serializable.class.isInstance(value)) { Logger.warn("Transaction-Storage can only store objects which implements the 'Seralizable' interface"); throw new MOADatabaseException("Transaction-Storage can only store objects which implements the 'Seralizable' interface", null); } //serialize the Assertion for Database storage byte[] data = SerializationUtils.serialize((Serializable) value); element.setAssertion(data); long id = new Random().nextLong(); element.setId(id); return element; } @Override public Object getRaw(String key) throws EAAFException { return searchInDatabase(key); } @Override public void putRaw(String key, Object element) throws EAAFException { // TODO Auto-generated method stub AssertionStore as = (AssertionStore)element; final int expTime = redisTemplate.getExpire(as.getArtifact(), TimeUnit.MILLISECONDS).intValue(); //AssertionStore element = searchInDatabase(oldKey); if (expTime < 0) { Logger.info("No transaction-data with oldKey:" + as.getArtifact() + " found. Process gets stopped."); throw new MOADatabaseException("No transaction-data with oldKey:" + as.getArtifact() + " found. Process gets stopped."); } redisTemplate.opsForValue().set(as.getArtifact(), new String(assertionStoreSerializer.serialize(element)),expTime,TimeUnit.MILLISECONDS); } }