/*
* 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;
}
}