Index: api/src/main/java/org/onehippo/repository/update/BaseNodeUpdateVisitor.java =================================================================== --- api/src/main/java/org/onehippo/repository/update/BaseNodeUpdateVisitor.java (revision 57135) +++ api/src/main/java/org/onehippo/repository/update/BaseNodeUpdateVisitor.java (working copy) @@ -29,6 +29,7 @@ protected Logger log; protected Map parametersMap; + protected NodeUpdateVisitorContext visitorContext; public void setLogger(Logger log) { this.log = log; @@ -38,6 +39,10 @@ this.parametersMap = parametersMap; } + public void setVisitorContext(NodeUpdateVisitorContext visitorContext) { + this.visitorContext = visitorContext; + } + @Override public void initialize(Session session) throws RepositoryException { } Index: api/src/main/java/org/onehippo/repository/update/NodeUpdateVisitorContext.java =================================================================== --- api/src/main/java/org/onehippo/repository/update/NodeUpdateVisitorContext.java (revision 0) +++ api/src/main/java/org/onehippo/repository/update/NodeUpdateVisitorContext.java (working copy) @@ -0,0 +1,43 @@ +/* + * Copyright 2015-2015 Hippo B.V. (http://www.onehippo.com) + * + * Licensed 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 org.onehippo.repository.update; + +/** + * Provided as an execution context to {@link NodeUpdateVisitor} instance in order to allow an {@link NodeUpdateVisitor} + * to be able to manually update skipped/updated/failed node count while being executed on single node iteration + * (e.g, in #doUpdate(node) method in a groovy updater script). + */ +public interface NodeUpdateVisitorContext { + + /** + * Manually report/increment the skipped count in the current execution. + */ + void reportSkipped(String path); + + /** + * Manually report/increment the updated count in the current execution. + *

+ * WARNING: this invocation may trigger committing or reverting the batch. + *

+ */ + void reportUpdated(String path); + + /** + * Manually report/increment the failed count in the current execution. + */ + void reportFailed(String path); + +} Property changes on: api/src/main/java/org/onehippo/repository/update/NodeUpdateVisitorContext.java ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: engine/src/main/java/org/onehippo/repository/update/UpdaterExecutor.java =================================================================== --- engine/src/main/java/org/onehippo/repository/update/UpdaterExecutor.java (revision 57135) +++ engine/src/main/java/org/onehippo/repository/update/UpdaterExecutor.java (working copy) @@ -100,6 +100,7 @@ if (updater instanceof BaseNodeUpdateVisitor) { ((BaseNodeUpdateVisitor) updater).setLogger(getLogger()); ((BaseNodeUpdateVisitor) updater).setParametersMap(jsonToParamsMap(updaterInfo.getParameters())); + ((BaseNodeUpdateVisitor) updater).setVisitorContext(new BaseNodeUpdateVisitorContext()); } updater.initialize(session); report.start(); @@ -418,11 +419,21 @@ private void commitBatchIfNeeded() throws RepositoryException { final boolean batchCompleted = report.getUpdateCount() != lastUpdateCount && report.getUpdateCount() % updaterInfo.getBatchSize() == 0; + + if (batchCompleted) { + if (getLogger().isDebugEnabled()) { + getLogger().debug("batch unit completion indicated. updateCount: {}, batchSize: {}", + report.getUpdateCount(), updaterInfo.getBatchSize()); + } + } + if (batchCompleted || report.isFinished()) { if (updaterInfo.isDryRun()) { + debug("discarding all pending changes currently recorded in this batch unit."); session.refresh(false); } else { try { + debug("saving all pending changes currently recorded in this batch unit."); session.save(); } catch (RepositoryException e) { error("Failed to save session", e); @@ -433,6 +444,7 @@ report.startBatch(); saveReport(); } + if (batchCompleted) { lastUpdateCount = report.getUpdateCount(); throttle(updaterInfo.getThrottle()); @@ -583,4 +595,32 @@ } return JSONObject.fromObject(paramsInJson); } + + class BaseNodeUpdateVisitorContext implements NodeUpdateVisitorContext { + + BaseNodeUpdateVisitorContext() { + } + + @Override + public void reportSkipped(String path) { + report.skipped(path); + } + + @Override + public void reportUpdated(String path) { + report.updated(path); + + try { + commitBatchIfNeeded(); + } catch (RepositoryException e) { + // log.error() instead of error() on purpose: report already saved + log.error(e.getClass().getName() + ": " + e.getMessage(), e); + } + } + + @Override + public void reportFailed(String path) { + report.failed(path); + } + } }