/* vim: set ts=2 et sw=2 cindent fo=qroca: */
package com.globant.katari.cas;
import org.apache.commons.lang.Validate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/** Transforms urls according to some specification.
*
* A source url can be transformed to a target url by changing the original
* port number or the path. Port tranformation can be specified directly or in
* a system property.
*/
public class UrlTransformer {
/** The class logger.
*/
private static Log log = LogFactory.getLog(UrlTransformer.class);
/** The replacement server name.
*
* If null, the server fragment of the original url is kept unmodified.
*/
private String replacementServerName = null;
/** The regular expression to match the path with.
*
* This can include grouping operations that can be referenced in the
* replacement. If null, the path is not matched to perform the replacement.
*/
private String pathRegex = null;
/** The path replacement string.
*
* If pathRegex has been specified, this can include references to the groups
* in the regex. If null, no replacement takes place. If pathRegex is null
* and this is not null, then the path takes this value.
*/
private String pathReplacement = null;
/** The port number tranformation specification.
*
* If it is null the port number is not transformed. If it starts with '+' or
* '-', then the number is added or substracted from the original port
* number. Otherwise, it must be a number specifying the port number.
*/
private String portSpecification = null;
/** Builds an identity url transformer.
*/
public UrlTransformer() {
}
/** Builds a new url transformer.
*
* @param theReplacementServerName The new server name. If null, the
* transformation leaves the server name as is.
*
* @param thePortSpecification The port specification used to contruct the
* port number of the target url based on the source url.
*
* The posible options are:
*
* -n: the target port is obtained substracting n from the source port.
*
* +n: the target port is obtained adding n to the source port.
*
* n: the target port is n.
*
* null: the target port is the same as the source port.
*
* Otherwise, it is the name of a system property that contains the port
* specification.
*
* @param thePathRegex The regular expression to match the path with. This
* can include grouping operations that can be referenced in the replacement.
* If null, the path is not matched to perform the replacement.
*
* @param thePathReplacement The path replacement string. The path
* replacement string. If pathRegex has been specified, this can include
* references to the groups in the regex. If null, no replacement takes
* place. If pathRegex is null and this is not null, then the path takes this
* value.
*/
public UrlTransformer(final String theReplacementServerName, final String
thePortSpecification, final String thePathRegex, final String
thePathReplacement) {
if (thePathRegex != null && thePathReplacement == null) {
throw new RuntimeException("The path replacemnet cannot be null if the"
+ " path regex is not.");
}
boolean isProperty = thePortSpecification != null
&& (!Character.isDigit(thePortSpecification.charAt(0))
&& !isPortRelative(thePortSpecification));
if (isProperty) {
portSpecification = System.getProperty(thePortSpecification);
} else {
portSpecification = thePortSpecification;
}
pathRegex = thePathRegex;
pathReplacement = thePathReplacement;
replacementServerName = theReplacementServerName;
}
/** Tranforms the url according to the specification.
*
* @param scheme The url protocol scheme of the source url, for example,
* http.
*
* @param serverName The server name of the source url.
*
* @param port The port number of the source url.
*
* @param path The url path of the source url.
*
* @return Returns the transformed url.
*/
public String transform(final String scheme, final String serverName,
final int port, final String path) {
if (log.isTraceEnabled()) {
log.trace("Entering transform('" + scheme + "', '" + serverName + "', "
+ port + ", '" + path + "'");
}
StringBuffer url = new StringBuffer();
url.append(scheme).append("://").append(transformServerName(serverName))
.append(":").append(transformPort(port));
String result = createUrl(url.toString(), transformPath(path));
if (log.isTraceEnabled()) {
log.trace("Leaving transform with " + url);
}
return result;
}
/** Transforms the server fragment of an url.
*
* This simply changes the original server name to the transformed server
* name, or it leaves it unmodified if the transformed server name is null.
*
* @param serverName The server name to transform. It cannot be null.
*
* @return Returns the transformed server name.
*/
private String transformServerName(final String serverName) {
Validate.notNull(serverName, "The server cannot be null");
String transformedServerName;
if (replacementServerName == null) {
// Do not modify the path.
transformedServerName = serverName;
} else {
// Transform the path according to the url.
transformedServerName = replacementServerName;
}
return transformedServerName;
}
/** Transforms the path according to pathRegex and pathReplacement.
*
* @param path The path to transform. It cannot be null.
*
* @return Returns the transformed path.
*/
private String transformPath(final String path) {
Validate.notNull(path, "The path cannot be null");
String transformedPath;
if (pathReplacement == null) {
// Do not modify the path.
transformedPath = path;
} else if (pathRegex == null) {
// Change the path without match.
transformedPath = pathReplacement;
} else {
// Transform the path according to the url.
transformedPath = path.replaceFirst(pathRegex, pathReplacement);
}
return transformedPath;
}
/** Transforms the port number.
*
* @param port The port number to transform. It cannot be null.
*
* @return Returns the transformed port.
*/
private int transformPort(final int port) {
Validate.notNull(port, "The port cannot be null");
int transformedPort = port;
if (portSpecification != null) {
int offset = intValue(portSpecification);
if (isPortRelative(portSpecification)) {
transformedPort += offset;
} else {
transformedPort = offset;
}
}
return transformedPort;
}
/** Converts a string to an integer, allowing an initial +.
*
* @param value The string value to convert. It cannot be null.
*
* @return Returns the integer represented in the value.
*/
private int intValue(final String value) {
Validate.notNull(value, "The value to convert cannot be null");
String conformingValue = value;
if (value.startsWith("+")) {
conformingValue = value.substring(1);
}
return Integer.valueOf(conformingValue).intValue();
}
/** Tests if the port specification is relative.
*
* @param port The port specification to test.
*
* @return Returns true if the port specification is relative.
*/
private boolean isPortRelative(final String port) {
if (port == null) {
return false;
}
return port.startsWith("-") || port.startsWith("+");
}
/** Creates a new url based on a base url and a path fragment.
*
* @param base The base url. It cannot be null.
*
* @param path The path fragment. It cannot be null.
*
* @return Returns the new url formed by the concatenation of the base url
* and the path fragment, including the '/' if necessary.
*/
private String createUrl(final String base, final String path) {
Validate.notNull(base, "The base url cannot be null");
Validate.notNull(path, "The path fragment cannot be null");
String result = base;
if (!result.endsWith("/") && !path.startsWith("/")) {
result += "/";
}
result += path;
return result;
}
}