package org.jembi.openhim; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jembi.openhim.RestfulHttpRequest.Scheme; import org.jembi.openhim.exception.DefaultChannelInvalidConfigException; import org.jembi.openhim.exception.URLMappingNotFoundException; import org.mule.api.MuleEventContext; import org.mule.api.MuleMessage; import org.mule.api.lifecycle.Callable; import org.mule.api.transport.PropertyScope; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.codec.binary.Base64; public class DefaultChannelComponent implements Callable { protected static List<URLMapping> mappings = new ArrayList<URLMapping>(); private static final String MAPPING_FILE = "/defaultchannel-mapping.json"; private static final String HTTP_AUTH_TYPE_BASIC = "basic"; @Override public Object onCall(MuleEventContext eventContext) throws Exception { if (mappings.size() < 1) { readMappings(); } MuleMessage msg = eventContext.getMessage(); RestfulHttpRequest req = (RestfulHttpRequest) msg.getPayload(); String actualPath = req.getPath(); URLMapping mapping = findURLMapping(req.getScheme(), actualPath); if (mapping == null) { throw new URLMappingNotFoundException("A URL mapping was not found for the URL: " + req.getPath()); } setMessagePropertiesFromMapping(req, msg, mapping); return msg; } protected void setMessagePropertiesFromMapping(RestfulHttpRequest request, MuleMessage msg, URLMapping mapping) throws DefaultChannelInvalidConfigException { msg.setProperty("http.host", mapping.getHost(), PropertyScope.OUTBOUND); msg.setProperty("http.port", mapping.getPort(), PropertyScope.OUTBOUND); if (mapping.getAuthType() != null) { if (mapping.getAuthType().equals(HTTP_AUTH_TYPE_BASIC)) { setBasicAuthProperty(msg, mapping); } } if (mapping.getPath()!=null || mapping.getPathTransform()!=null) { setRequestPath(request, mapping); } msg.setProperty("path", request.buildUrlWithRequestParams(), PropertyScope.OUTBOUND); } private void setBasicAuthProperty(MuleMessage msg, URLMapping mapping) { String username = mapping.getUsername(); String password = mapping.getPassword(); byte[] encodedBytes = Base64.encodeBase64((username + ":" + password).getBytes()); String authHeader = "Basic " + new String(encodedBytes); msg.setProperty("http.auth", authHeader, PropertyScope.OUTBOUND); } private void setRequestPath(RestfulHttpRequest request, URLMapping mapping) throws DefaultChannelInvalidConfigException { if (mapping.getPath()!=null && mapping.getPathTransform()!=null) { throw new DefaultChannelInvalidConfigException("Cannot specify both path and pathTransform"); } String path = null; if (mapping.getPathTransform()!=null) { path = transformPath(request.getPath(), mapping.getPathTransform()); } else { path = mapping.getPath(); } request.setPath(path); } private String transformPath(String path, String sPattern) throws DefaultChannelInvalidConfigException { //replace all \/'s with a temporary ~ so that we don't split on those String pattern = sPattern.replaceAll("\\\\/", "~"); String[] sub = pattern.split("/"); if (sub.length<2 || !sub[0].equals("s")) { throw new DefaultChannelInvalidConfigException("Malformed pathTransform expression. Expected \"s/from/to\""); } String from = sub[1].replaceAll("~", "/"); String to = (sub.length>2) ? sub[2] : ""; to = to.replaceAll("~", "/"); return path.replaceAll(from, to); } protected URLMapping findURLMapping(Scheme scheme, String actualPath) { for (URLMapping mapping : mappings) { if (scheme.equals(Scheme.HTTP) && !"true".equalsIgnoreCase(mapping.getAllowUnsecured())) { continue; } String urlPattern = mapping.getUrlPattern(); Pattern p = Pattern.compile(urlPattern); Matcher m = p.matcher(actualPath); boolean match = m.matches(); if (match) { return mapping; } } return null; } protected void readMappings() throws IOException, JsonParseException, JsonMappingException { // Read mapping out of JSON file ObjectMapper mapper = new ObjectMapper(); JsonFactory factory = new JsonFactory(); InputStream is = this.getClass().getResourceAsStream(MAPPING_FILE); JsonParser jp = factory.createParser(is); jp.nextToken(); mappings.clear(); while (jp.nextToken() == JsonToken.START_OBJECT) { URLMapping mapping = mapper.readValue(jp, URLMapping.class); mappings.add(mapping); } } public static class URLMapping { private String urlPattern; private String host; private String port; private String username; private String password; private String authType; private String allowUnsecured; private String path; private String pathTransform; @Override public boolean equals(Object obj) { if (!(obj instanceof URLMapping)) { return false; } URLMapping mapping = (URLMapping) obj; if (urlPattern.equals(mapping.getUrlPattern()) && host.equals(mapping.getHost()) && port.equals(mapping.getPort())) { return true; } return false; } public String getAuthType() { return authType; } public void setAuthType(String authType) { this.authType = authType; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getUrlPattern() { return urlPattern; } public void setUrlPattern(String urlPattern) { this.urlPattern = urlPattern; } public String getHost() { return host; } public void setHost(String host) { this.host = host; } public String getPort() { return port; } public void setPort(String port) { this.port = port; } public String getAllowUnsecured() { return allowUnsecured; } public void setAllowUnsecured(String allowUnsecured) { this.allowUnsecured = allowUnsecured; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } public String getPathTransform() { return pathTransform; } public void setPathTransform(String pathTransform) { this.pathTransform = pathTransform; } } }