/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ro.nextreports.server.update; import java.util.ArrayList; import java.util.List; import javax.jcr.Node; import javax.jcr.NodeIterator; import javax.jcr.PathNotFoundException; import javax.jcr.Property; import javax.jcr.query.QueryResult; import org.apache.jackrabbit.util.ISO9075; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Required; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; import org.springframework.extensions.jcr.JcrTemplate; import org.springframework.extensions.jcr.SessionFactory; import ro.nextreports.server.StorageConstants; /** * @author Decebal Suiu */ public class StorageUpdater implements InitializingBean { private static final Logger LOG = LoggerFactory.getLogger(StorageUpdater.class); protected String updatesClassNamesPrefix = StorageUpdate.class.getName(); private PlatformTransactionManager transactionManager; private SessionFactory sessionFactory; private JcrTemplate jcrTemplate; @Required public void setTransactionManager(PlatformTransactionManager transactionManager) { this.transactionManager = transactionManager; } @Required public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } public void afterPropertiesSet() throws Exception { jcrTemplate = new JcrTemplate(sessionFactory); executeUpdates(); } private void executeUpdates() throws Exception { TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) { try { long storageVersion = getStorageVersion(); LOG.info("Current storage version is " + storageVersion); List<StorageUpdate> updates = getUpdates(storageVersion); int updateSize = updates.size(); LOG.info("Found " + updateSize + " updates"); if (updateSize > 0) { long lastUpdateVersion = updates.get(updateSize -1).getVersion(); if (lastUpdateVersion < storageVersion) { // TODO (lock the storage, abnormal situation) new RuntimeException("Current version greater than last update version"); } performUpdates(updates); } } catch (Exception e) { e.printStackTrace(); LOG.error(e.getMessage(), e); transactionStatus.setRollbackOnly(); } } }); } private long getStorageVersion() throws Exception { if (jcrTemplate.itemExists(StorageConstants.NEXT_SERVER_ROOT)) { Node node = (Node) jcrTemplate.getItem(StorageConstants.NEXT_SERVER_ROOT); return node.getProperty("version").getLong(); } return -1; } private void incrementStorageVersion() throws Exception { Node node = (Node) jcrTemplate.getItem(StorageConstants.NEXT_SERVER_ROOT); node.setProperty("version", getStorageVersion() + 1); jcrTemplate.save(); } private void resetFirstUsageDates() throws Exception { LOG.info("Reset firstUsage.date for all users"); String statement = "/jcr:root" + ISO9075.encodePath(StorageConstants.USERS_ROOT) + "//*[@className='ro.nextreports.server.domain.UserPreferences']"; QueryResult queryResult = jcrTemplate.query(statement); NodeIterator nodes = queryResult.getNodes(); LOG.info("Found " + nodes.getSize() + " user preferences nodes"); while (nodes.hasNext()) { Node node = nodes.nextNode(); Node pNode = node.getNode("preferences"); try { Property property = pNode.getProperty("firstUsage.date"); if (property.getValue() != null) { LOG.info(" removed firstUsage.date = " + property.getString() + " for user " + node.getParent().getName()); String s = null; pNode.setProperty("firstUsage.date", s); } } catch (PathNotFoundException ex) { // nothing to do } } jcrTemplate.save(); } private void performUpdates(List<StorageUpdate> updates) throws Exception { for (StorageUpdate update : updates) { long updateVersion = update.getVersion(); LOG.info("Updating storage version to " + updateVersion); update.setTemplate(jcrTemplate); update.executeUpdate(); LOG.info("Updated storage version to " + updateVersion); incrementStorageVersion(); resetFirstUsageDates(); } } private List<StorageUpdate> getUpdates(long storageVersion) throws Exception { List<StorageUpdate> updates = new ArrayList<StorageUpdate>(); try { while (true) { storageVersion++; String updateClassName = updatesClassNamesPrefix + storageVersion; updates.add((StorageUpdate) Class.forName(updateClassName).newInstance()); } } catch (ClassNotFoundException e) { // expected after last update } return updates; } }