HostConverter.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.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.common.utility.WaarpStringUtils;
import org.waarp.openr66.pojo.Host;
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.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import static org.waarp.common.file.FileUtils.*;
import static org.waarp.openr66.protocol.configuration.Configuration.*;
import static org.waarp.openr66.protocol.http.restv2.RestConstants.HostFields.*;
import static org.waarp.openr66.protocol.http.restv2.errors.RestErrors.*;
/**
* A collection of utility methods to convert {@link Host} objects to their
* corresponding {@link ObjectNode}
* and vice-versa.
*/
public final class HostConverter {
/**
* Makes the default constructor of this utility class inaccessible.
*/
private HostConverter() throws InstantiationException {
throw new InstantiationException(
getClass().getName() + " cannot be instantiated.");
}
// ########################### INNER CLASSES ################################
/**
* Represents all the possible ways to sort a list of host objects.
*/
public enum Order {
/**
* By hostID, in ascending order.
*/
ascId(new Comparator<Host>() {
@Override
public final int compare(final Host t1, final Host t2) {
return t1.getHostid().compareTo(t2.getHostid());
}
}),
/**
* By hostID, in descending order.
*/
descId(new Comparator<Host>() {
@Override
public final int compare(final Host t1, final Host t2) {
return -t1.getHostid().compareTo(t2.getHostid());//NOSONAR
}
}),
/**
* By address, in ascending order.
*/
ascAddress(new Comparator<Host>() {
@Override
public final int compare(final Host t1, final Host t2) {
return t1.getAddress().compareTo(t2.getAddress());//NOSONAR
}
}),
/**
* By address, in descending order.
*/
descAddress(new Comparator<Host>() {
@Override
public final int compare(final Host t1, final Host t2) {
return -t1.getAddress().compareTo(t2.getAddress());//NOSONAR
}
});
/**
* The comparator used to sort the list of RestHost objects.
*/
public final Comparator<Host> comparator;
Order(final Comparator<Host> comparator) {
this.comparator = comparator;
}
}
// ########################### PUBLIC METHODS ###############################
/**
* Converts the given {@link Host} object into an {@link ObjectNode}.
*
* @param host the host to convert
*
* @return the converted ObjectNode
*/
public static ObjectNode hostToNode(final Host host) {
final ObjectNode node = JsonHandler.createObjectNode();
node.put(HOST_NAME, host.getHostid());
node.put(ADDRESS, host.getAddress());
node.put(PORT, host.getPort());
node.put(PASSWORD, host.getHostkey());
node.put(IS_SSL, host.isSSL());
node.put(IS_CLIENT, host.isClient());
node.put(IS_ADMIN, host.isAdmin());
node.put(IS_ACTIVE, host.isActive());
node.put(IS_PROXY, host.isProxified());
return node;
}
/**
* Converts the given {@link ObjectNode} into a {@link Host} object.
*
* @param object the ObjectNode to convert
*
* @return the corresponding Host object
*
* @throws RestErrorException if the given ObjectNode does not
* represent a Host object
* @throws InternalServerErrorException if an unexpected error
* occurred
*/
public static Host nodeToNewHost(final ObjectNode object) {
Host emptyHost = null;
try {
emptyHost =
new Host(null, null, -1, null, false, false, false, false, true);
} catch (final WaarpDatabaseSqlException e) {
SysErrLogger.FAKE_LOGGER.syserr(e);
}
return nodeToUpdatedHost(object, emptyHost);
}
/**
* Returns the given {@link Host} object updated with the values defined in
* the {@link ObjectNode} parameter.
* All fields missing in the JSON object will stay unchanged in the updated
* host entry.
*
* @param object the ObjectNode to convert.
* @param oldHost the host entry to update.
*
* @return the updated host entry
*
* @throws RestErrorException if the given ObjectNode does not
* represent a Host object
* @throws InternalServerErrorException if an unexpected error
* occurred
*/
public static Host nodeToUpdatedHost(final ObjectNode object,
final Host oldHost) {
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(HOST_NAME)) {
if (value.isTextual()) {
if (oldHost.getHostid() == null) {
oldHost.setHostid(value.asText());
} else if (!oldHost.getHostid().equals(value.asText())) {
errors.add(FIELD_NOT_ALLOWED(HOST_NAME));
}
} else {
errors.add(ILLEGAL_FIELD_VALUE(name, value.toString()));
}
} else if (name.equalsIgnoreCase(ADDRESS)) {
if (value.isTextual()) {
oldHost.setAddress(value.asText());
} else {
errors.add(ILLEGAL_FIELD_VALUE(name, value.toString()));
}
} else if (name.equalsIgnoreCase(PORT)) {
if (value.canConvertToInt() && value.asInt() >= 0 &&
value.asInt() < ZERO_COPY_CHUNK_SIZE) {
oldHost.setPort(value.asInt());
} else {
errors.add(ILLEGAL_FIELD_VALUE(name, value.toString()));
}
} else if (name.equalsIgnoreCase(PASSWORD)) {
if (value.isTextual()) {
oldHost.setHostkey(encryptPassword(value.asText()));
} else {
errors.add(ILLEGAL_FIELD_VALUE(name, value.toString()));
}
} else if (name.equalsIgnoreCase(IS_SSL)) {
if (value.isBoolean()) {
oldHost.setSSL(value.asBoolean());
} else {
errors.add(ILLEGAL_FIELD_VALUE(name, value.toString()));
}
} else if (name.equalsIgnoreCase(IS_CLIENT)) {
if (value.isBoolean()) {
oldHost.setClient(value.asBoolean());
} else {
errors.add(ILLEGAL_FIELD_VALUE(name, value.toString()));
}
} else if (name.equalsIgnoreCase(IS_ADMIN)) {
if (value.isBoolean()) {
oldHost.setAdmin(value.asBoolean());
} else {
errors.add(ILLEGAL_FIELD_VALUE(name, value.toString()));
}
} else if (name.equalsIgnoreCase(IS_ACTIVE)) {
if (value.isBoolean()) {
oldHost.setActive(value.asBoolean());
} else {
errors.add(ILLEGAL_FIELD_VALUE(name, value.toString()));
}
} else if (name.equalsIgnoreCase(IS_PROXY)) {
if (value.isBoolean()) {
oldHost.setProxified(value.asBoolean());
} else {
errors.add(ILLEGAL_FIELD_VALUE(name, value.toString()));
}
} else {
errors.add(UNKNOWN_FIELD(name));
}
}
errors.addAll(checkRequiredFields(oldHost));
if (errors.isEmpty()) {
return oldHost;
} else {
throw new RestErrorException(errors);
}
}
// ########################## PRIVATE METHODS ###############################
/**
* Encrypts the given password String using the server's cryptographic key.
*
* @param password the password to encrypt
*
* @return the encrypted password
*
* @throws InternalServerErrorException If an error occurred when
* encrypting the password.
*/
private static byte[] encryptPassword(final String password) {
try {
return configuration.getCryptoKey().cryptToHex(password)
.getBytes(WaarpStringUtils.UTF8);
} catch (final Exception e) {
throw new InternalServerErrorException(
"Failed to encrypt the host password", e);
}
}
/**
* List all missing required fields. This method returns a list of {@link
* RestError} representing all the
* errors encountered when checking the given host's required fields. If all
* required fields have indeed been
* initialized, an empty list is returned.
*
* @param host the host entry to check
*
* @return the list of encountered errors
*/
private static List<RestError> checkRequiredFields(final Host host) {
final List<RestError> errors = new ArrayList<RestError>();
if (ParametersChecker.isEmpty(host.getHostid())) {
errors.add(MISSING_FIELD(HOST_NAME));
}
if (ParametersChecker.isEmpty(host.getAddress())) {
errors.add(MISSING_FIELD(ADDRESS));
}
if (host.getPort() == -1) {
errors.add(MISSING_FIELD(PORT));
}
if (host.getHostkey() == null || host.getHostkey().length == 0) {
errors.add(MISSING_FIELD(PASSWORD));
}
return errors;
}
}