diff --git a/config/alfresco/bootstrap-context.xml b/config/alfresco/bootstrap-context.xml index f3f7df4c55..8bbdd6869c 100644 --- a/config/alfresco/bootstrap-context.xml +++ b/config/alfresco/bootstrap-context.xml @@ -494,11 +494,13 @@ + + diff --git a/config/alfresco/messages/system-messages.properties b/config/alfresco/messages/system-messages.properties index 6542beb8ba..66fc98b023 100644 --- a/config/alfresco/messages/system-messages.properties +++ b/config/alfresco/messages/system-messages.properties @@ -94,8 +94,12 @@ system.portcheck.err.empty_port=The value for {0} port property cannot be empty. system.portcheck.err.parse_port=Unable to parse value for {0} port property: {1}. system.portcheck.err.port_out_of_bounds=The port chosen for {0} is outside the required range (1, 65535): {1}. system.portcheck.err.ports_out_of_bounds=The following ports chosen for {0} are outside the required range (1, 65535): {1}. -system.portcheck.err.port_in_use=The port chosen for {0} is already in use: {1}. -system.portcheck.err.ports_in_use=The following ports chosen for {0} are already in use: {1}. +system.portcheck.err.port_in_use=The port chosen for {0} is already in use or you don''t have permission to use it: {1}. +system.portcheck.err.host_port_in_use=The address chosen for {0} is already in use or you don''t have permission: {1}:{2}. +system.portcheck.err.ports_in_use=The following ports chosen for {0} are already in use or you don''t have permission to use them: {1}. +system.portcheck.err.unknown_or_wrong_host=The hostname chosen for {0} is unknown or misspelled: {1}. +system.portcheck.err.networking_error=In subsystem {0} a networking error was encountered: {1}. +system.portcheck.err.ports_with_networking_errors=The following ports chosen for {0} have networking errors: {1}. # License system.license.msg.unknown=Unknown diff --git a/source/java/org/alfresco/repo/management/subsystems/AbstractPropertyBackedBean.java b/source/java/org/alfresco/repo/management/subsystems/AbstractPropertyBackedBean.java index db4ecb0ee8..e053d003e5 100644 --- a/source/java/org/alfresco/repo/management/subsystems/AbstractPropertyBackedBean.java +++ b/source/java/org/alfresco/repo/management/subsystems/AbstractPropertyBackedBean.java @@ -321,9 +321,9 @@ public abstract class AbstractPropertyBackedBean implements PropertyBackedBean, /** * Check properties for invalid values using {@link SubsystemEarlyPropertyChecker}s * @param properties - * @throws InvalidPropertyValueException + * @return The complete error message in case of exceptions or empty string otherwise */ - public void performEarlyPropertyChecks(Map properties) throws InvalidPropertyValueException + public String performEarlyPropertyChecks(Map properties) { if (properties != null && !properties.isEmpty() && earlyPropertyCheckers != null) { @@ -339,7 +339,16 @@ public abstract class AbstractPropertyBackedBean implements PropertyBackedBean, if (propertyChecker != null) { - propertyChecker.checkPropertyValue(property, properties.get(property)); + if (propertyChecker.getPairedPropertyName() != null + && properties.containsKey(propertyChecker.getPairedPropertyName())) + { + propertyChecker.checkPropertyValue(property, properties.get(property), + properties.get(propertyChecker.getPairedPropertyName())); + } + else + { + propertyChecker.checkPropertyValue(property, properties.get(property), null); + } } } catch (InvalidPropertyValueException ipve) @@ -363,9 +372,11 @@ public abstract class AbstractPropertyBackedBean implements PropertyBackedBean, allExceptionsMessages += ipve.getLocalizedMessage(); } - throw new InvalidPropertyValueException(allExceptionsMessages); + return allExceptionsMessages; } } + + return ""; } /** diff --git a/source/java/org/alfresco/repo/management/subsystems/PortEarlyPropertyChecker.java b/source/java/org/alfresco/repo/management/subsystems/PortEarlyPropertyChecker.java index 69904797c2..e05580e633 100644 --- a/source/java/org/alfresco/repo/management/subsystems/PortEarlyPropertyChecker.java +++ b/source/java/org/alfresco/repo/management/subsystems/PortEarlyPropertyChecker.java @@ -18,6 +18,7 @@ */ package org.alfresco.repo.management.subsystems; +import java.io.IOException; import java.util.Arrays; import org.alfresco.util.PortUtil; import org.apache.commons.logging.Log; @@ -37,23 +38,42 @@ public class PortEarlyPropertyChecker implements SubsystemEarlyPropertyChecker private static final String PORT_OUT_OF_BOUNDS_MESSAGE = "system.portcheck.err.port_out_of_bounds"; private static final String PORTS_OUT_OF_BOUNDS_MESSAGE = "system.portcheck.err.ports_out_of_bounds"; private static final String PORT_IN_USE_MESSAGE = "system.portcheck.err.port_in_use"; + private static final String HOST_PORT_IN_USE_MESSAGE = "system.portcheck.err.host_port_in_use"; private static final String PORTS_IN_USE_MESSAGE = "system.portcheck.err.ports_in_use"; - + private static final String UNKNOWN_OR_WRONG_HOST_MESSAGE = "system.portcheck.err.unknown_or_wrong_host"; + private static final String NETWORKING_ERROR_MESSAGE = "system.portcheck.err.networking_error"; + private static final String PORTS_WITH_NETWORKING_ERRORS_MESSAGE = "system.portcheck.err.ports_with_networking_errors"; + private static final Log logger = LogFactory.getLog(PortEarlyPropertyChecker.class); private final String subsystemName; + private final String requiredPairedPropertyName; + private final boolean hasMultiplePorts; private final boolean shouldCheckForBlockedPort; /** - * Create a new {@link PortEarlyPropertyChecker}. + * Create a new {@link PortEarlyPropertyChecker} w/o a paired property name. * @param subsystemName Name of the subsystem; used for custom error messages. * @param hasMultiplePorts Specify if the property value that will be checked is a list of ports (they must be separated by ","). * @param shouldCheckForBlockedPort Enable/disable checking for port-already-in-use (i.e.: disable this for remote ports). */ public PortEarlyPropertyChecker(String subsystemName, boolean hasMultiplePorts, boolean shouldCheckForBlockedPort) + { + this(subsystemName, null, hasMultiplePorts, shouldCheckForBlockedPort); + } + + /** + * Create a new {@link PortEarlyPropertyChecker}. + * @param subsystemName Name of the subsystem; used for custom error messages. + * @param requiredPairedPropertyName Name of the required paired property (see {@link SubsystemEarlyPropertyChecker#getPairedPropertyName()}). + * @param hasMultiplePorts Specify if the property value that will be checked is a list of ports (they must be separated by ","). + * @param shouldCheckForBlockedPort Enable/disable checking for port-already-in-use (i.e.: disable this for remote ports). + */ + public PortEarlyPropertyChecker(String subsystemName, String requiredPairedPropertyName, boolean hasMultiplePorts, boolean shouldCheckForBlockedPort) { this.subsystemName = subsystemName; + this.requiredPairedPropertyName = requiredPairedPropertyName; this.hasMultiplePorts = hasMultiplePorts; this.shouldCheckForBlockedPort = shouldCheckForBlockedPort; } @@ -62,16 +82,19 @@ public class PortEarlyPropertyChecker implements SubsystemEarlyPropertyChecker * Implementation of checkPropertyValue() method for port checking. * @param propertyName Name of the property. * @param propertyValue Port value; if this contains multiple ports they must be separated by ",". + * @param pairedPropertyValue - Value of the paired property * @throws InvalidPropertyValueException Raised if any of the checks fail. */ @Override - public void checkPropertyValue(String propertyName, String propertyValue) throws InvalidPropertyValueException + public void checkPropertyValue(String propertyName, String propertyValue, String pairedPropertyValue) throws InvalidPropertyValueException { if (propertyValue == null || propertyValue.isEmpty()) { - throw new InvalidPropertyValueException(PORT_CANT_BE_EMPTY_MESSAGE, new String[] { subsystemName }); + createLogAndThrowAnInvalidPropertyValueException(PORT_CANT_BE_EMPTY_MESSAGE, new String[] { subsystemName }); } + String host = pairedPropertyValue; + try { if (!hasMultiplePorts) @@ -80,27 +103,38 @@ public class PortEarlyPropertyChecker implements SubsystemEarlyPropertyChecker if (portNumber < 1 || portNumber > 65535) { - InvalidPropertyValueException portOutOfBoundsException = - new InvalidPropertyValueException(PORT_OUT_OF_BOUNDS_MESSAGE, new String[] { subsystemName, "" + portNumber }); - - if (logger.isErrorEnabled()) - { - logger.error(portOutOfBoundsException.getLocalizedMessage()); - } - - throw portOutOfBoundsException; + createLogAndThrowAnInvalidPropertyValueException(PORT_OUT_OF_BOUNDS_MESSAGE, new String[] { subsystemName, "" + portNumber }); } - else if (shouldCheckForBlockedPort && !PortUtil.isPortFree(portNumber)) + else if (shouldCheckForBlockedPort) { - InvalidPropertyValueException portInUseException = - new InvalidPropertyValueException(PORT_IN_USE_MESSAGE, new String[] { subsystemName, "" + portNumber }); - - if (logger.isErrorEnabled()) + try { - logger.error(portInUseException.getLocalizedMessage()); + PortUtil.checkPort(portNumber, host); + } + catch (IOException ioe) + { + if (ioe instanceof java.net.BindException) + { + if (host == null || "0.0.0.0".equals(host)) + { + createLogAndThrowAnInvalidPropertyValueException(PORT_IN_USE_MESSAGE, new String[] { subsystemName, "" + portNumber }); + } + else + { + createLogAndThrowAnInvalidPropertyValueException(HOST_PORT_IN_USE_MESSAGE, new String[] { subsystemName, host, + "" + portNumber }); + } + } + else if (host != null && ioe instanceof java.net.UnknownHostException) + { + createLogAndThrowAnInvalidPropertyValueException(UNKNOWN_OR_WRONG_HOST_MESSAGE, new String[] { subsystemName, "" + host }); + } + else + { + createLogAndThrowAnInvalidPropertyValueException(NETWORKING_ERROR_MESSAGE, + new String[] { subsystemName, ioe.getLocalizedMessage() }); + } } - - throw portInUseException; } } else @@ -109,6 +143,7 @@ public class PortEarlyPropertyChecker implements SubsystemEarlyPropertyChecker String portsInUse = ""; String portsOutOfBounds = ""; + String portsWithNetworkingErrors = ""; for (String portStr : ports) { @@ -116,21 +151,26 @@ public class PortEarlyPropertyChecker implements SubsystemEarlyPropertyChecker if (portNumber < 1 || portNumber > 65535) { - if (!portsOutOfBounds.equals("")) - { - portsOutOfBounds += ", "; - } - - portsOutOfBounds += portNumber; + portsOutOfBounds = appendToErrorString(portsOutOfBounds, ", ", "" + portNumber); } - else if (shouldCheckForBlockedPort && !PortUtil.isPortFree(portNumber)) + else if (shouldCheckForBlockedPort) { - if (!portsInUse.equals("")) + try { - portsInUse += ", "; + PortUtil.checkPort(portNumber, host); + } + catch (IOException ioe) + { + if (ioe instanceof java.net.BindException) + { + portsInUse = appendToErrorString(portsInUse, ", ", "" + portNumber); + } + else + { + portsWithNetworkingErrors = appendToErrorString(portsWithNetworkingErrors, " ; ", + portNumber + ": " + ioe.getLocalizedMessage()); + } } - - portsInUse += portNumber; } } @@ -138,8 +178,8 @@ public class PortEarlyPropertyChecker implements SubsystemEarlyPropertyChecker if (!portsOutOfBounds.equals("")) { - String portsOutOfBoundsDisplayMessage = - resolveMessage(PORTS_OUT_OF_BOUNDS_MESSAGE, new String[] { subsystemName, portsOutOfBounds }); + String portsOutOfBoundsDisplayMessage = resolveMessage(PORTS_OUT_OF_BOUNDS_MESSAGE, new String[] { subsystemName, + portsOutOfBounds }); completeErrorDisplayMessage += portsOutOfBoundsDisplayMessage; } @@ -148,12 +188,15 @@ public class PortEarlyPropertyChecker implements SubsystemEarlyPropertyChecker { String portsInUseDisplayMessage = resolveMessage(PORTS_IN_USE_MESSAGE, new String[] { subsystemName, portsInUse }); - if (!completeErrorDisplayMessage.equals("")) - { - completeErrorDisplayMessage += " | "; - } + completeErrorDisplayMessage = appendToErrorString(completeErrorDisplayMessage, " | ", portsInUseDisplayMessage); + } - completeErrorDisplayMessage += portsInUseDisplayMessage; + if (!portsWithNetworkingErrors.equals("")) + { + String portsWithNetworkingErrorsDisplayMessage = resolveMessage(PORTS_WITH_NETWORKING_ERRORS_MESSAGE, new String[] { + subsystemName, portsWithNetworkingErrors }); + + completeErrorDisplayMessage = appendToErrorString(completeErrorDisplayMessage, " | ", portsWithNetworkingErrorsDisplayMessage); } if (!completeErrorDisplayMessage.equals("")) @@ -169,18 +212,34 @@ public class PortEarlyPropertyChecker implements SubsystemEarlyPropertyChecker } catch (NumberFormatException nfe) { - InvalidPropertyValueException unableToParseException = - new InvalidPropertyValueException(UNABLE_TO_PARSE_PORT_MESSAGE, new String[] { subsystemName, propertyValue }); - - if (logger.isErrorEnabled()) - { - logger.error(unableToParseException.getLocalizedMessage()); - } - - throw unableToParseException; + createLogAndThrowAnInvalidPropertyValueException(UNABLE_TO_PARSE_PORT_MESSAGE, new String[] { subsystemName, propertyValue }); } } + private String appendToErrorString(String stringToAppendTo, String separator, String valueToAppend) + { + if (!stringToAppendTo.equals("")) + { + stringToAppendTo = stringToAppendTo + separator; + } + + stringToAppendTo = stringToAppendTo + valueToAppend; + + return stringToAppendTo; + } + + private void createLogAndThrowAnInvalidPropertyValueException(String message, String[] messageParams) + { + InvalidPropertyValueException invalidPropertyValueException = new InvalidPropertyValueException(message, messageParams); + + if (logger.isErrorEnabled()) + { + logger.error(invalidPropertyValueException.getLocalizedMessage()); + } + + throw invalidPropertyValueException; + } + private String resolveMessage(String messageId, Object[] params) { String message = I18NUtil.getMessage(messageId, params); @@ -197,4 +256,10 @@ public class PortEarlyPropertyChecker implements SubsystemEarlyPropertyChecker return message; } + + @Override + public String getPairedPropertyName() + { + return requiredPairedPropertyName; + } } diff --git a/source/java/org/alfresco/repo/management/subsystems/SubsystemEarlyPropertyChecker.java b/source/java/org/alfresco/repo/management/subsystems/SubsystemEarlyPropertyChecker.java index 3490fc1a4c..26a812b8b7 100644 --- a/source/java/org/alfresco/repo/management/subsystems/SubsystemEarlyPropertyChecker.java +++ b/source/java/org/alfresco/repo/management/subsystems/SubsystemEarlyPropertyChecker.java @@ -26,11 +26,20 @@ package org.alfresco.repo.management.subsystems; */ public interface SubsystemEarlyPropertyChecker { + /** + * Get the (optional) paired property name (e.g., if we want to check a port + * number we might want to do that together with a specific local address). + * + * @return The paired property name. + */ + String getPairedPropertyName(); + /** * Check if a subsystem property is valid. * @param propertyName * @param propertyValue + * @param pairedPropertyValue * @throws InvalidPropertyValueException */ - void checkPropertyValue(String propertyName, String propertyValue) throws InvalidPropertyValueException; + void checkPropertyValue(String propertyName, String propertyValue, String pairedPropertyValue) throws InvalidPropertyValueException; } diff --git a/source/java/org/alfresco/util/PortUtil.java b/source/java/org/alfresco/util/PortUtil.java index 2d84a561dd..dd09902914 100644 --- a/source/java/org/alfresco/util/PortUtil.java +++ b/source/java/org/alfresco/util/PortUtil.java @@ -19,7 +19,9 @@ package org.alfresco.util; import java.io.IOException; +import java.net.InetAddress; import java.net.ServerSocket; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -35,20 +37,22 @@ public class PortUtil /** * Check if specified port is free. * @param port Port number to check. - * @return true if port is free or false if it's already in use. + * @param host A local address to bind to; if null, "" or "0.0.0.0" then all local addresses will be considered. */ - public static boolean isPortFree(int port) + public static void checkPort(int port, String host) throws IOException { - boolean isFree = true; ServerSocket serverSocket = null; try { - serverSocket = new ServerSocket(port); - } - catch (IOException ioe) - { - isFree = false; + if (host != null && !host.equals("") && !"0.0.0.0".equals(host.trim())) + { + serverSocket = new ServerSocket(port, 0, InetAddress.getByName(host.trim())); + } + else + { + serverSocket = new ServerSocket(port); + } } finally { @@ -67,7 +71,5 @@ public class PortUtil } } } - - return isFree; } } diff --git a/source/test-resources/subsystem-test-context.xml b/source/test-resources/subsystem-test-context.xml index 40c90c43cc..823ab03d50 100644 --- a/source/test-resources/subsystem-test-context.xml +++ b/source/test-resources/subsystem-test-context.xml @@ -20,19 +20,26 @@ - - - - - + + + + + + - + + + + + + +