TransferConverter.java
/*
* This file is part of Waarp Project (named also Waarp or GG).
*
* Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
* tags. See the COPYRIGHT.txt in the distribution for a full listing of
* individual contributors.
*
* All Waarp Project is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* Waarp . If not, see <http://www.gnu.org/licenses/>.
*/
package org.waarp.openr66.protocol.http.restv2.converters;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.joda.time.DateTime;
import org.waarp.common.database.exception.WaarpDatabaseException;
import org.waarp.common.database.exception.WaarpDatabaseSqlException;
import org.waarp.common.json.JsonHandler;
import org.waarp.common.logging.SysErrLogger;
import org.waarp.common.utility.ParametersChecker;
import org.waarp.openr66.dao.DAOFactory;
import org.waarp.openr66.dao.HostDAO;
import org.waarp.openr66.dao.RuleDAO;
import org.waarp.openr66.dao.exception.DAOConnectionException;
import org.waarp.openr66.dao.exception.DAONoDataException;
import org.waarp.openr66.pojo.Rule;
import org.waarp.openr66.pojo.Transfer;
import org.waarp.openr66.pojo.UpdatedInfo;
import org.waarp.openr66.protocol.configuration.Configuration;
import org.waarp.openr66.protocol.http.restv2.converters.RuleConverter.ModeTrans;
import org.waarp.openr66.protocol.http.restv2.errors.RestError;
import org.waarp.openr66.protocol.http.restv2.errors.RestErrorException;
import javax.ws.rs.InternalServerErrorException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import static org.waarp.common.file.FileUtils.*;
import static org.waarp.openr66.dao.database.DBTransferDAO.*;
import static org.waarp.openr66.protocol.http.restv2.RestConstants.*;
import static org.waarp.openr66.protocol.http.restv2.RestConstants.TransferFields.*;
import static org.waarp.openr66.protocol.http.restv2.errors.RestErrors.*;
/**
* A collection of utility methods to convert {@link Transfer} objects to their
* corresponding
* {@link ObjectNode} and vice-versa.
*/
public final class TransferConverter {
/**
* Makes the default constructor of this utility class inaccessible.
*/
private TransferConverter() throws InstantiationException {
throw new InstantiationException(
getClass().getName() + " cannot be instantiated.");
}
// ########################### INNER CLASSES ################################
/**
* All the possible ways to order a list of transfer objects.
*/
public enum Order {
/**
* By transferId, in ascending order.
*/
ascId(ID_FIELD, true),
/**
* By transferId, in descending order.
*/
descId(ID_FIELD, false),
/**
* By fileName, in ascending order.
*/
ascFile(ORIGINAL_NAME_FIELD, true),
/**
* By fileName, in descending order.
*/
descFile(ORIGINAL_NAME_FIELD, false),
/**
* By starting date, in ascending order.
*/
ascStart(TRANSFER_START_FIELD, true),
/**
* By starting date, in descending order.
*/
descStart(TRANSFER_START_FIELD, false),
/**
* By end date, in ascending order.
*/
ascStop(TRANSFER_STOP_FIELD, true),
/**
* By end date, in descending order.
*/
descStop(TRANSFER_STOP_FIELD, false);
/**
* The name of the database column used for sorting.
*/
public final String column;
/**
* If the order is ascending or descending.
*/
public final boolean ascend;
Order(final String column, final boolean ascend) {
this.column = column;
this.ascend = ascend;
}
}
// ########################## PUBLIC METHODS ################################
/**
* Returns an {@link ObjectNode} representing the {@link Transfer} object
* given as parameter.
*
* @param transfer the Transfer object to serialize
*
* @return the corresponding ObjectNode
*/
public static ObjectNode transferToNode(final Transfer transfer) {
final ObjectNode node = JsonHandler.createObjectNode();
node.put(TRANSFER_ID, transfer.getId());
node.put(GLOBAL_STEP, transfer.getGlobalStep().toString());
node.put(GLOBAL_LAST_STEP, transfer.getLastGlobalStep().toString());
node.put(STEP, transfer.getStep());
node.put(RANK, transfer.getRank());
node.put(UPDATED_INFO, transfer.getUpdatedInfo().toString());
node.put(STEP_STATUS, transfer.getStepStatus().toString());
node.put(ERROR_CODE, transfer.getInfoStatus().code);
node.put(ERROR_MESSAGE, transfer.getInfoStatus().getMesg());
node.put(ORIGINAL_FILENAME, transfer.getOriginalName());
node.put(FILENAME, transfer.getFilename());
node.put(RULE, transfer.getRule());
node.put(BLOCK_SIZE, transfer.getBlockSize());
node.put(FILE_INFO, transfer.getFileInfo());
node.put(TRANSFER_INFO, transfer.getTransferInfo());
node.put(START, new DateTime(transfer.getStart()).toString());
node.put(STOP, new DateTime(transfer.getStop()).toString());
node.put(REQUESTED, transfer.getRequested());
node.put(REQUESTER, transfer.getRequester());
node.put(RETRIEVE, transfer.getRetrieveMode());
return node;
}
/**
* Initialize a {@link Transfer} object using the values of the given {@link
* ObjectNode}.
*
* @param object the ObjectNode to convert
*
* @return the new Transfer object
*
* @throws RestErrorException if the given ObjectNode does not
* represent a Transfer object
* @throws InternalServerErrorException if an unexpected error
* occurred
*/
public static Transfer nodeToNewTransfer(final ObjectNode object) {
Transfer defaultTransfer = null;
try {
defaultTransfer =
new Transfer(null, null, -1, false, null, null, ZERO_COPY_CHUNK_SIZE);
} catch (final WaarpDatabaseSqlException e) {
SysErrLogger.FAKE_LOGGER.syserr(e);
throw new IllegalArgumentException(e);
}
defaultTransfer.setRequester(serverName());
defaultTransfer.setOwnerRequest(serverName());
defaultTransfer.setBlockSize(Configuration.configuration.getBlockSize());
defaultTransfer.setTransferInfo("{}");
defaultTransfer.setStart(new Timestamp(DateTime.now().getMillis()));
final Transfer transfer = parseNode(object, defaultTransfer);
ModeTrans mode;
RuleDAO ruleDAO = null;
try {
ruleDAO = DAO_FACTORY.getRuleDAO(true);
final Rule rule = ruleDAO.select(transfer.getRule());
mode = ModeTrans.fromCode(rule.getMode());
} catch (final DAOConnectionException e) {
throw new InternalServerErrorException(e);
} catch (final DAONoDataException e) {
throw new InternalServerErrorException(e);
} finally {
DAOFactory.closeDAO(ruleDAO);
}
transfer.setRetrieveMode(
mode == ModeTrans.receive || mode == ModeTrans.receiveMD5);
transfer.setTransferMode(mode.code);
transfer.setStop(transfer.getStart());
transfer.setUpdatedInfo(UpdatedInfo.TOSUBMIT);
return transfer;
}
// ######################### PRIVATE METHODS ################################
/**
* Tells if the given rule exists in the database.
*
* @param rule the name of the rule
*
* @return {@code true} if the rule exists, {@code false} otherwise.
*/
private static boolean ruleExists(final String rule) {
RuleDAO ruleDAO = null;
try {
ruleDAO = DAO_FACTORY.getRuleDAO(true);
return ruleDAO.exist(rule);
} catch (final DAOConnectionException e) {
throw new InternalServerErrorException(e);
} finally {
DAOFactory.closeDAO(ruleDAO);
}
}
/**
* Tells if the given host exists in the database.
*
* @param host the name of the host
*
* @return {@code true} if the host exists, {@code false} otherwise.
*/
private static boolean hostExists(final String host) {
HostDAO hostDAO = null;
try {
hostDAO = DAO_FACTORY.getHostDAO(true);
return hostDAO.exist(host);
} catch (final DAOConnectionException e) {
throw new InternalServerErrorException(e);
} finally {
DAOFactory.closeDAO(hostDAO);
}
}
/**
* Tells if the given host is allowed to use given rule.
*
* @param host the name of the host
* @param rule the name of the rule
*
* @return {@code true} if the host is allowed to use the rule, {@code
* false} otherwise
*/
private static boolean canUseRule(final String host, final String rule) {
RuleDAO ruleDAO = null;
try {
ruleDAO = DAO_FACTORY.getRuleDAO(true);
final List<String> hostIds = ruleDAO.select(rule).getHostids();
return !hostIds.isEmpty() && !hostIds.contains(host);
} catch (final DAOConnectionException e) {
throw new InternalServerErrorException(e);
} catch (final DAONoDataException e) {
throw new InternalServerErrorException(e);
} finally {
DAOFactory.closeDAO(ruleDAO);
}
}
/**
* Returns a list of {@link RestError} corresponding to all the fields
* required to initialize a transfer that
* are missing from the given {@link Transfer} object. If no fields are
* missing, an empty list is returned.
*
* @param transfer the Transfer object to check.
*
* @return the list of all missing fields
*/
private static List<RestError> checkRequiredFields(final Transfer transfer) {
final List<RestError> errors = new ArrayList<RestError>();
if (ParametersChecker.isEmpty(transfer.getRule())) {
errors.add(MISSING_FIELD(RULE));
}
if (ParametersChecker.isEmpty(transfer.getOriginalName())) {
errors.add(MISSING_FIELD(FILENAME));
}
if (ParametersChecker.isEmpty(transfer.getRequested())) {
errors.add(MISSING_FIELD(REQUESTED));
}
return errors;
}
/**
* Fills the fields of the given {@link Transfer} object with the values
* extracted from the {@link ObjectNode}
* parameter, and returns the result.
*
* @param object the ObjectNode from which the values should be
* extracted
* @param transfer the Transfer object whose fields will be filled
*
* @return the filled Transfer object
*
* @throws RestErrorException if the given ObjectNode does not
* represent a Transfer object.
*/
private static Transfer parseNode(final ObjectNode object,
final Transfer transfer) {
final List<RestError> errors = new ArrayList<RestError>();
final Iterator<Map.Entry<String, JsonNode>> fields = object.fields();
while (fields.hasNext()) {
final Map.Entry<String, JsonNode> field = fields.next();
final String name = field.getKey();
final JsonNode value = field.getValue();
if (name.equalsIgnoreCase(RULE)) {
if (value.isTextual()) {
if (ruleExists(value.asText())) {
transfer.setRule(value.asText());
} else {
errors.add(UNKNOWN_RULE(value.asText()));
}
} else {
errors.add(ILLEGAL_FIELD_VALUE(name, value.toString()));
}
} else if (name.equalsIgnoreCase(FILENAME)) {
if (value.isTextual()) {
transfer.setOriginalName(value.asText());
transfer.setFilename(value.asText());
} else {
errors.add(ILLEGAL_FIELD_VALUE(name, value.toString()));
}
} else if (name.equalsIgnoreCase(REQUESTED)) {
if (value.isTextual()) {
if (hostExists(value.asText())) {
transfer.setRequested(value.asText());
try {
transfer.setRequester(
Configuration.configuration.getHostId(value.asText()));
} catch (final WaarpDatabaseException e) {
// Ignore !!
}
} else {
errors.add(UNKNOWN_HOST(value.asText()));
}
} else {
errors.add(ILLEGAL_FIELD_VALUE(name, value.toString()));
}
} else if (name.equalsIgnoreCase(BLOCK_SIZE)) {
if (value.canConvertToInt() && value.asInt() > 0) {
transfer.setBlockSize(value.asInt());
} else {
errors.add(ILLEGAL_FIELD_VALUE(name, value.toString()));
}
} else if (name.equalsIgnoreCase(FILE_INFO)) {
if (value.isTextual()) {
transfer.setFileInfo(value.asText());
} else {
errors.add(ILLEGAL_FIELD_VALUE(name, value.toString()));
}
} else if (name.equalsIgnoreCase(TRANSFER_INFO)) {
if (value.isTextual()) {
transfer.setTransferInfo(value.asText());
} else {
errors.add(ILLEGAL_FIELD_VALUE(name, value.toString()));
}
} else if (name.equalsIgnoreCase(START)) {
if (value.isTextual()) {
try {
final DateTime start = DateTime.parse(value.asText());
if (start.isBeforeNow()) {
errors.add(ILLEGAL_FIELD_VALUE(name, value.asText()));
} else {
transfer.setStart(new Timestamp(start.getMillis()));
}
} catch (final IllegalArgumentException e) {
errors.add(ILLEGAL_FIELD_VALUE(name, value.asText()));
}
} else {
errors.add(ILLEGAL_FIELD_VALUE(name, value.toString()));
}
}
}
// check that both hosts are allowed to use the transfer rule
final String rule = transfer.getRule();
final String requested = transfer.getRequested();
final String requester = transfer.getRequester();
if (rule != null && !requested.isEmpty() && canUseRule(requested, rule)) {
errors.add(RULE_NOT_ALLOWED(requested, rule));
}
if (rule != null && !requester.isEmpty() && canUseRule(requester, rule)) {
errors.add(RULE_NOT_ALLOWED(requester, rule));
}
errors.addAll(checkRequiredFields(transfer));
if (errors.isEmpty()) {
return transfer;
} else {
throw new RestErrorException(errors);
}
}
}