/**
* Licensed to JumpMind Inc under one or more contributor
* license agreements. See the NOTICE file distributed
* with this work for additional information regarding
* copyright ownership. JumpMind Inc licenses this file
* to you under the GNU General Public License, version 3.0 (GPLv3)
* (the "License"); you may not use this file except in compliance
* with the License.
*
* You should have received a copy of the GNU General Public License,
* version 3.0 (GPLv3) along with this library; if not, see
* <http://www.gnu.org/licenses/>.
*
* 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.jumpmind.symmetric.service.impl;
import java.util.ArrayList;
import java.util.List;
import org.jumpmind.db.sql.mapper.NumberMapper;
import org.jumpmind.symmetric.ISymmetricEngine;
import org.jumpmind.symmetric.common.Constants;
import org.jumpmind.symmetric.io.stage.IStagedResource;
import org.jumpmind.symmetric.io.stage.IStagedResource.State;
import org.jumpmind.symmetric.io.stage.IStagingManager;
import org.jumpmind.symmetric.model.BatchAck;
import org.jumpmind.symmetric.model.BatchAckResult;
import org.jumpmind.symmetric.model.Channel;
import org.jumpmind.symmetric.model.OutgoingBatch;
import org.jumpmind.symmetric.model.OutgoingBatch.Status;
import org.jumpmind.symmetric.service.IAcknowledgeService;
import org.jumpmind.symmetric.service.IOutgoingBatchService;
import org.jumpmind.symmetric.service.IRegistrationService;
import org.jumpmind.symmetric.statistic.RouterStats;
import org.jumpmind.symmetric.transport.IAcknowledgeEventListener;
/**
* @see IAcknowledgeService
*/
public class AcknowledgeService extends AbstractService implements IAcknowledgeService {
private ISymmetricEngine engine;
public AcknowledgeService(ISymmetricEngine engine) {
super(engine.getParameterService(), engine.getSymmetricDialect());
this.engine = engine;
setSqlMap(new AcknowledgeServiceSqlMap(symmetricDialect.getPlatform(),
createSqlReplacementTokens()));
}
public BatchAckResult ack(final BatchAck batch) {
IRegistrationService registrationService = engine.getRegistrationService();
IStagingManager stagingManager = engine.getStagingManager();
IOutgoingBatchService outgoingBatchService = engine.getOutgoingBatchService();
BatchAckResult result = new BatchAckResult(batch);
for (IAcknowledgeEventListener listener : engine.getExtensionService().getExtensionPointList(IAcknowledgeEventListener.class)) {
listener.onAcknowledgeEvent(batch);
}
if (batch.getBatchId() == Constants.VIRTUAL_BATCH_FOR_REGISTRATION) {
if (batch.getStatus() == Status.OK) {
registrationService.markNodeAsRegistered(batch.getNodeId());
}
} else {
OutgoingBatch outgoingBatch = outgoingBatchService
.findOutgoingBatch(batch.getBatchId(), batch.getNodeId());
Status status = batch.getStatus();
if (outgoingBatch != null) {
// Allow an outside system/user to indicate that a batch
// is OK.
if (outgoingBatch.getStatus() != Status.OK &&
outgoingBatch.getStatus() != Status.IG) {
outgoingBatch.setStatus(status);
outgoingBatch.setErrorFlag(batch.getStatus() == Status.ER);
} else {
// clearing the error flag in case the user set the batch
// status to OK
Status oldStatus = outgoingBatch.getStatus();
outgoingBatch.setStatus(Status.OK);
outgoingBatch.setErrorFlag(false);
log.info("Batch {} for {} was set to {}. Updating the status to OK",
new Object[] { batch.getBatchId(), batch.getNodeId(), oldStatus.name() });
}
if (batch.isIgnored()) {
outgoingBatch.incrementIgnoreCount();
}
outgoingBatch.setNetworkMillis(batch.getNetworkMillis());
outgoingBatch.setFilterMillis(batch.getFilterMillis());
outgoingBatch.setLoadMillis(batch.getDatabaseMillis());
outgoingBatch.setSqlCode(batch.getSqlCode());
outgoingBatch.setSqlState(batch.getSqlState());
outgoingBatch.setSqlMessage(batch.getSqlMessage());
if (batch.getStatus() == Status.ER && batch.getErrorLine() != 0) {
List<Number> ids = sqlTemplate.query(getSql("selectDataIdSql"),
new NumberMapper(), outgoingBatch.getBatchId());
if (ids.size() >= batch.getErrorLine()) {
outgoingBatch.setFailedDataId(ids.get((int) batch.getErrorLine() - 1)
.longValue());
}
}
if (status == Status.ER) {
log.error(
"The outgoing batch {} failed{}",
outgoingBatch.getNodeBatchId(), batch.getSqlMessage() != null ? ". " + batch.getSqlMessage() : "");
RouterStats routerStats = engine.getStatisticManager().getRouterStatsByBatch(batch.getBatchId());
if (routerStats != null) {
log.info("Router stats for batch " + outgoingBatch.getBatchId() + ": " + routerStats.toString());
}
} else if (!outgoingBatch.isCommonFlag()) {
IStagedResource stagingResource = stagingManager.find(
Constants.STAGING_CATEGORY_OUTGOING, outgoingBatch.getNodeId(),
outgoingBatch.getBatchId());
if (stagingResource != null) {
stagingResource.setState(State.DONE);
}
}
outgoingBatchService.updateOutgoingBatch(outgoingBatch);
if (status == Status.OK) {
Channel channel = engine.getConfigurationService().getChannel(outgoingBatch.getChannelId());
if (channel != null && channel.isFileSyncFlag()){
/* Acknowledge the file_sync in case the file needs deleted. */
engine.getFileSyncService().acknowledgeFiles(outgoingBatch);
}
engine.getStatisticManager().removeRouterStatsByBatch(batch.getBatchId());
}
} else {
log.error("Could not find batch {}-{} to acknowledge as {}", new Object[] {batch.getNodeId(), batch.getBatchId(),
status.name()});
result.setOk(false);
}
}
return result;
}
public List<BatchAckResult> ack(List<BatchAck> batches) {
List<BatchAckResult> results = new ArrayList<BatchAckResult>();
for (BatchAck batch:batches) {
results.add(ack(batch));
}
return results;
}
}