org.jumpmind.symmetric.service.impl.AbstractService.java Source code

Java tutorial

Introduction

Here is the source code for org.jumpmind.symmetric.service.impl.AbstractService.java

Source

/**
 * 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.io.BufferedReader;
import java.io.EOFException;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.sql.SQLException;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.jumpmind.db.platform.IDatabasePlatform;
import org.jumpmind.db.sql.ISqlTemplate;
import org.jumpmind.db.sql.ISqlTransaction;
import org.jumpmind.symmetric.SymmetricException;
import org.jumpmind.symmetric.common.ParameterConstants;
import org.jumpmind.symmetric.common.TableConstants;
import org.jumpmind.symmetric.db.ISymmetricDialect;
import org.jumpmind.symmetric.model.BatchAck;
import org.jumpmind.symmetric.model.IncomingBatch;
import org.jumpmind.symmetric.model.Node;
import org.jumpmind.symmetric.model.NodeSecurity;
import org.jumpmind.symmetric.model.OutgoingBatch;
import org.jumpmind.symmetric.model.OutgoingBatch.Status;
import org.jumpmind.symmetric.service.IAcknowledgeService;
import org.jumpmind.symmetric.service.IParameterService;
import org.jumpmind.symmetric.service.IService;
import org.jumpmind.symmetric.transport.IOutgoingWithResponseTransport;
import org.jumpmind.symmetric.transport.ITransportManager;
import org.jumpmind.util.AppUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

abstract public class AbstractService implements IService {

    protected final Logger log = LoggerFactory.getLogger(getClass());

    protected IParameterService parameterService;

    protected ISymmetricDialect symmetricDialect;

    protected ISqlTemplate sqlTemplate;

    protected IDatabasePlatform platform;

    protected String tablePrefix;

    private ISqlMap sqlMap;

    private Set<String> logOnce = new HashSet<String>();

    public AbstractService(IParameterService parameterService, ISymmetricDialect symmetricDialect) {
        this.symmetricDialect = symmetricDialect;
        this.parameterService = parameterService;
        this.tablePrefix = parameterService.getTablePrefix();
        this.platform = symmetricDialect.getPlatform();
        this.sqlTemplate = symmetricDialect.getPlatform().getSqlTemplate();
    }

    protected Date maxDate(Date... dates) {
        Date date = null;
        if (dates != null) {
            for (Date d : dates) {
                if (d != null) {
                    if (date == null || d.after(date)) {
                        date = d;
                    }
                }
            }
        }

        return date;
    }

    protected void setSqlMap(ISqlMap sqlMap) {
        this.sqlMap = sqlMap;
    }

    public ISqlTemplate getJdbcTemplate() {
        return symmetricDialect.getPlatform().getSqlTemplate();
    }

    synchronized public void synchronize(Runnable runnable) {
        runnable.run();
    }

    protected boolean isSet(Object value) {
        if (value != null && value.toString().equals("1")) {
            return true;
        } else {
            return false;
        }
    }

    @SuppressWarnings("unchecked")
    protected SQLException unwrapSqlException(Throwable e) {
        List<Throwable> exs = ExceptionUtils.getThrowableList(e);
        for (Throwable throwable : exs) {
            if (throwable instanceof SQLException) {
                return (SQLException) throwable;
            }
        }
        return null;
    }

    protected Map<String, String> createSqlReplacementTokens() {
        Map<String, String> replacementTokens = createSqlReplacementTokens(this.tablePrefix,
                symmetricDialect.getPlatform().getDatabaseInfo().getDelimiterToken());
        replacementTokens.putAll(symmetricDialect.getSqlReplacementTokens());
        return replacementTokens;
    }

    protected static Map<String, String> createSqlReplacementTokens(String tablePrefix, String quotedIdentifier) {
        Map<String, String> map = new HashMap<String, String>();
        List<String> tables = TableConstants.getTablesWithoutPrefix();
        for (String table : tables) {
            map.put(table,
                    String.format("%s%s%s", tablePrefix, StringUtils.isNotBlank(tablePrefix) ? "_" : "", table));
        }
        return map;
    }

    public String getSql(String... keys) {
        if (sqlMap != null) {
            return sqlMap.getSql(keys);
        } else {
            return null;
        }
    }

    public IParameterService getParameterService() {
        return parameterService;
    }

    public ISymmetricDialect getSymmetricDialect() {
        return symmetricDialect;
    }

    public String getTablePrefix() {
        return tablePrefix;
    }

    protected void close(ISqlTransaction transaction) {
        if (transaction != null) {
            transaction.close();
        }
    }

    protected Set<String> toNodeIds(Set<Node> nodes) {
        return toNodeIds(nodes, null);
    }

    protected Set<String> toNodeIds(Set<Node> nodes, Set<String> nodeIds) {
        nodeIds = nodeIds == null ? new HashSet<String>(nodes.size()) : nodeIds;
        for (Node node : nodes) {
            nodeIds.add(node.getNodeId());
        }
        return nodeIds;
    }

    protected String getRootMessage(Exception ex) {
        Throwable cause = ExceptionUtils.getRootCause(ex);
        if (cause == null) {
            cause = ex;
        }
        return cause.getMessage();
    }

    protected boolean isCalledFromSymmetricAdminTool() {
        boolean adminTool = false;
        StackTraceElement[] trace = Thread.currentThread().getStackTrace();
        for (StackTraceElement stackTraceElement : trace) {
            adminTool |= stackTraceElement.getClassName().equals("org.jumpmind.symmetric.SymmetricAdmin");
        }
        return adminTool;
    }

    protected String buildBatchWhere(List<String> nodeIds, List<String> channels, List<?> statuses) {
        boolean containsErrorStatus = statuses.contains(OutgoingBatch.Status.ER)
                || statuses.contains(IncomingBatch.Status.ER);
        boolean containsIgnoreStatus = statuses.contains(OutgoingBatch.Status.IG)
                || statuses.contains(IncomingBatch.Status.IG);

        StringBuilder where = new StringBuilder();
        boolean needsAnd = false;
        if (nodeIds.size() > 0) {
            where.append("node_id in (:NODES)");
            needsAnd = true;
        }

        if (channels.size() > 0) {
            if (needsAnd) {
                where.append(" and ");
            }
            where.append("channel_id in (:CHANNELS)");
            needsAnd = true;
        }
        if (statuses.size() > 0) {
            if (needsAnd) {
                where.append(" and ");
            }
            where.append("(status in (:STATUSES)");

            if (containsErrorStatus) {
                where.append(" or error_flag = 1 ");
            }

            if (containsIgnoreStatus) {
                where.append(" or ignore_count > 0 ");
            }

            where.append(")");

            needsAnd = true;
        }

        if (where.length() > 0) {
            where.insert(0, " where ");
        }
        return where.toString();
    }

    /**
     * Try a configured number of times to get the ACK through.
     */
    protected void sendAck(Node remote, Node local, NodeSecurity localSecurity, List<IncomingBatch> list,
            ITransportManager transportManager) throws IOException {
        Exception error = null;
        int sendAck = -1;
        int numberOfStatusSendRetries = parameterService.getInt(ParameterConstants.DATA_LOADER_NUM_OF_ACK_RETRIES);
        for (int i = 0; i < numberOfStatusSendRetries && sendAck != HttpURLConnection.HTTP_OK; i++) {
            try {
                sendAck = transportManager.sendAcknowledgement(remote, list, local, localSecurity.getNodePassword(),
                        parameterService.getRegistrationUrl());
            } catch (IOException ex) {
                error = ex;
            } catch (RuntimeException ex) {
                error = ex;
            }
            if (sendAck != HttpURLConnection.HTTP_OK) {
                log.warn("Ack was not sent successfully on try number {}.  {}", i + 1,
                        error != null ? error.getMessage() : "");
                if (i < numberOfStatusSendRetries - 1) {
                    AppUtils.sleep(
                            parameterService.getLong(ParameterConstants.DATA_LOADER_TIME_BETWEEN_ACK_RETRIES));
                } else if (error instanceof RuntimeException) {
                    throw (RuntimeException) error;
                } else if (error instanceof IOException) {
                    throw (IOException) error;
                } else {
                    throw new IOException(Integer.toString(sendAck));
                }
            }
        }
    }

    protected List<BatchAck> readAcks(List<OutgoingBatch> batches, IOutgoingWithResponseTransport transport,
            ITransportManager transportManager, IAcknowledgeService acknowledgeService) throws IOException {

        Set<Long> batchIds = new HashSet<Long>(batches.size());
        for (OutgoingBatch outgoingBatch : batches) {
            if (outgoingBatch.getStatus() == OutgoingBatch.Status.LD) {
                batchIds.add(outgoingBatch.getBatchId());
            }
        }

        BufferedReader reader = transport.readResponse();
        String ackString = reader.readLine();
        String ackExtendedString = reader.readLine();

        log.debug("Reading ack: {}", ackString);
        log.debug("Reading extend ack: {}", ackExtendedString);

        String line = null;
        do {
            line = reader.readLine();
            if (line != null) {
                log.info("Read another unexpected line {}", line);
            }
        } while (line != null);

        if (StringUtils.isBlank(ackString)) {
            throw new SymmetricException(
                    "Did not receive an acknowledgement for the batches sent.  "
                            + "The 'ack string' was: '%s' and the 'extended ack string' was: '%s'",
                    ackString, ackExtendedString);
        }

        List<BatchAck> batchAcks = transportManager.readAcknowledgement(ackString, ackExtendedString);

        long batchIdInError = Long.MAX_VALUE;
        for (BatchAck batchInfo : batchAcks) {
            batchIds.remove(batchInfo.getBatchId());
            if (batchInfo.getStatus() == Status.ER) {
                batchIdInError = batchInfo.getBatchId();
            }
            log.debug("Saving ack: {}, {}", batchInfo.getBatchId(), batchInfo.getStatus());
            acknowledgeService.ack(batchInfo);
        }

        for (Long batchId : batchIds) {
            if (batchId < batchIdInError) {
                log.error("We expected but did not receive an ack for batch {}", batchId);
            }
        }

        return batchAcks;
    }

    protected void logOnce(String message) {
        if (!logOnce.contains(message)) {
            logOnce.add(message);
            log.info(message);
        }
    }

    protected boolean isStreamClosedByClient(Exception ex) {
        if (ExceptionUtils.indexOfType(ex, EOFException.class) >= 0) {
            return true;
        } else {
            return false;
        }
    }

}