diff options
author | antelder <antelder@13f79535-47bb-0310-9956-ffa450edef68> | 2012-04-12 09:14:28 +0000 |
---|---|---|
committer | antelder <antelder@13f79535-47bb-0310-9956-ffa450edef68> | 2012-04-12 09:14:28 +0000 |
commit | bad1f71da1978610806427e2e2a2f96a2fb5b88e (patch) | |
tree | 334e5c38ba5cc33fd853e37c640728ec020862fb /sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca | |
parent | 57c2c65b9d869afc1be14926da2ece51ab52231b (diff) |
Tag 2.0 RC1
git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@1325166 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca')
12 files changed, 1186 insertions, 0 deletions
diff --git a/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/HTTPCacheContext.java b/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/HTTPCacheContext.java new file mode 100644 index 0000000000..08faf6a14b --- /dev/null +++ b/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/HTTPCacheContext.java @@ -0,0 +1,280 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.common.http; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import javax.servlet.http.HttpServletRequest; + +/** + * A class to store cache settings for Atom and HTTP requests and responses. + * + * Predicates are statements that work in conjunction with + * ETags and LastModified dates to determine if a precondition + * or postcondition is satisfied. + * See HTTP specification for how predicates wrk: + * http://tools.ietf.org/html/rfc2616 + * Example predicates in HTTP include If-Match, If-None-Match, + * If-Modified-Since, If-Unmodified-Since, If-Range. + * + * @version $Rev$ $Date$ + */ +public class HTTPCacheContext { + public static final SimpleDateFormat RFC822DateFormat = new SimpleDateFormat( "EEE, dd MMM yyyy HH:mm:ss Z" ); // RFC 822 date time + + public boolean enabled; + public String eTag; + public String lastModified; + public Date lastModifiedDate; + public boolean ifModifiedSince; + public boolean ifUnmodifiedSince; + public boolean ifMatch; + public boolean ifNoneMatch; + public boolean ifRange; + + + /** + * Enabled is true whenever ETag, LastModified, or predicate is set. + * @return the enabled + */ + public boolean isEnabled() { + return enabled; + } + + /** + * @param enabled the enabled to set + */ + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + /** + * An ETag is a unique ID for an item. It changes when + * a field in the item or the update date changes. + * See HTTP specification for how ETags work: + * http://tools.ietf.org/html/rfc2616 + * @return the eTag + */ + public String getETag() { + return eTag; + } + + /** + * @param tag the eTag to set + */ + public void setETag(String tag) { + eTag = tag; + enabled = true; + } + + /** + * The LastModified date is the time the item was last + * changed. See HTTP specification for how ETags work: + * http://tools.ietf.org/html/rfc2616 + * @return the lastModified + */ + public String getLastModified() { + return lastModified; + } + + /** + * The LastModified date is the time the item was last + * changed. See HTTP specification for how ETags work: + * http://tools.ietf.org/html/rfc2616 + * @return the lastModified + */ + public Date getLastModifiedAsDate() { + return lastModifiedDate; + } + + /** + * @param lastModified the lastModified to set + */ + public void setLastModified(String lastModified) throws java.text.ParseException { + this.lastModified = lastModified; + // Catch date formatting on input to help debugging. + lastModifiedDate = RFC822DateFormat.parse( lastModified ); + enabled = true; + } + + /** + * @param lastModified the lastModified to set + */ + public void setLastModified(Date updated) { + this.lastModified = RFC822DateFormat.format( updated ); + lastModifiedDate = updated; + enabled = true; + } + + /** + * @return the ifModifedSince + */ + public boolean isIfModifiedSince() { + return ifModifiedSince; + } + + /** + * @param ifModifedSince the ifModifedSince to set + */ + public void setIfModifiedSince(boolean ifModifiedSince) { + this.ifModifiedSince = ifModifiedSince; + if ( ifModifiedSince ) { + enabled = true; + } + } + + /** + * @return the ifUnModifiedSince + */ + public boolean isIfUnmodifiedSince() { + return ifUnmodifiedSince; + } + + /** + * @param ifUnModifiedSince the ifUnModifiedSince to set + */ + public void setIfUnmodifiedSince(boolean ifUnmodifiedSince) { + this.ifUnmodifiedSince = ifUnmodifiedSince; + if ( ifUnmodifiedSince ) { + enabled = true; + } + } + + /** + * @return the ifMatch + */ + public boolean isIfMatch() { + return ifMatch; + } + + /** + * @param ifMatch the ifMatch to set + */ + public void setIfMatch(boolean ifMatch) { + this.ifMatch = ifMatch; + if ( ifMatch ) { + enabled = true; + } + } + + /** + * @return the ifNoneMatch + */ + public boolean isIfNoneMatch() { + return ifNoneMatch; + } + + /** + * @param ifNoneMatch the ifNoneMatch to set + */ + public void setIfNoneMatch(boolean ifNoneMatch) { + this.ifNoneMatch = ifNoneMatch; + if ( ifNoneMatch ) { + enabled = true; + } + } + + /** + * @return the ifRange + */ + public boolean isIfRange() { + return ifRange; + } + + /** + * @param ifRange the ifRange to set + */ + public void setIfRange(boolean ifRange) { + this.ifRange = ifRange; + if ( ifRange ) { + enabled = true; + } + } + + /** + * Gets the cache context information (ETag, LastModified, predicates) from the Http request. + * @param request + * @return + */ + public static HTTPCacheContext createCacheContextFromRequest( HttpServletRequest request ) throws java.text.ParseException { + HTTPCacheContext context = new HTTPCacheContext(); + + String eTag = request.getHeader( "If-Match" ); + if ( eTag != null ) { + context.setETag( eTag ); + context.setIfMatch( true ); + } + eTag = request.getHeader( "If-None-Match" ); + if ( eTag != null ) { + context.setETag( eTag ); + context.setIfNoneMatch( true ); + } + String lastModifiedString = request.getHeader( "If-Modified-Since" ); + if ( lastModifiedString != null ) { + context.setLastModified( lastModifiedString ); + context.setIfModifiedSince( true ); + } + lastModifiedString = request.getHeader( "If-Unmodified-Since" ); + if ( lastModifiedString != null ) { + context.setLastModified( lastModifiedString ); + context.setIfUnmodifiedSince( true ); + } + lastModifiedString = request.getHeader( "If-Range" ); + if ( lastModifiedString != null ) { + context.setLastModified( lastModifiedString ); + context.setIfRange( true ); + } + return context; + } + + public String toString() { + final String PREDPREFIX = ", predicates="; + StringBuffer sb = new StringBuffer(PREDPREFIX); + if ( ifMatch || ifNoneMatch || ifModifiedSince || ifUnmodifiedSince || ifRange ) { + if ( ifMatch ) { + if ( sb.length() > PREDPREFIX.length() ) sb.append( ", "); + sb.append("If-Match"); + } + if ( ifNoneMatch ) { + if ( sb.length() > PREDPREFIX.length() ) sb.append( ", "); + sb.append("If-None-Match"); + } + if ( ifModifiedSince ) { + if ( sb.length() > PREDPREFIX.length() ) sb.append( ", "); + sb.append("If-Modified-Since"); + } + if ( ifUnmodifiedSince ) { + if ( sb.length() > PREDPREFIX.length() ) sb.append( ", "); + sb.append("If-UnModified-Since"); + } + if ( ifRange ) { + if ( sb.length() > PREDPREFIX.length() ) sb.append( ", "); + sb.append("If-Range"); + } + } else { + sb.append("null"); + } + + return "eTag=" + eTag + ", lastModified=" + lastModified + + sb.toString(); + } + +} diff --git a/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/HTTPConstants.java b/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/HTTPConstants.java new file mode 100644 index 0000000000..056e7f5f54 --- /dev/null +++ b/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/HTTPConstants.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.common.http; + +/** + * HTTP generic constants + * + * @version $Rev$ $Date$ + */ +public interface HTTPConstants { + public static final String CHARACTER_ENCODING_UTF8 = "UTF-8"; + + public static final String HTM = "htm"; + public static final String HTML = "html"; + public static final String JAVASCRIPT = "js"; + + public static final String HTML_CONTENT_TYPE = "text/html"; + public static final String JAVASCRIPT_CONTENT_TYPE = "text/javascript"; +} diff --git a/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/HTTPContentTypeMapper.java b/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/HTTPContentTypeMapper.java new file mode 100644 index 0000000000..035e7c0fa6 --- /dev/null +++ b/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/HTTPContentTypeMapper.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.common.http; + +import java.util.HashMap; +import java.util.Map; + +/** + * Utility class to provide default HTTP Content-Type header + * based on resource type + * + * @version $Rev$ $Date$ + */ +public class HTTPContentTypeMapper implements HTTPConstants{ + private static Map<String, String> contentTypeTable = new HashMap<String, String>(); + + static { + contentTypeTable.put(HTM, HTML_CONTENT_TYPE); + contentTypeTable.put(HTML, HTML_CONTENT_TYPE); + contentTypeTable.put(JAVASCRIPT, JAVASCRIPT_CONTENT_TYPE); + } + + /** + * Provided a resource path, identify default content-type based on the resource extension + * @param resourcePath + * @return + */ + public static String getContentType(String resourcePath) { + return contentTypeTable.get(getResourceType(resourcePath)); + } + + /** + * Utility function to calculate file type based on its extension + * Useful to map HTTP content-type based on file extension + * @param resource the resource/file name + * @return the resource type/extension + */ + private static String getResourceType(String resource) { + return resource.substring(resource.lastIndexOf(".") + 1); + } +} diff --git a/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/HTTPContext.java b/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/HTTPContext.java new file mode 100644 index 0000000000..3324be6ef1 --- /dev/null +++ b/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/HTTPContext.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.common.http; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * HTTP Context used as binding context in HTTP related bindings + * + * @version $Rev$ $Date$ + */ +public class HTTPContext { + private HttpServletRequest request; + private HttpServletResponse response; + + public HttpServletRequest getHttpRequest() { + return request; + } + public void setHttpRequest(HttpServletRequest request) { + this.request = request; + } + public HttpServletResponse getHttpResponse() { + return response; + } + public void setHttpResponse(HttpServletResponse response) { + this.response = response; + } +} diff --git a/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/HTTPHeader.java b/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/HTTPHeader.java new file mode 100644 index 0000000000..63f62d1e5c --- /dev/null +++ b/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/HTTPHeader.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.common.http; + +/** + * Wrapper for HTTP header name/value pair + * + * @version $Rev$ $Date$ + */ + +public class HTTPHeader { + private String name; + private String value; + + public HTTPHeader() { + super(); + } + + public HTTPHeader(String name, String value) { + super(); + this.name = name; + this.value = value; + } + + /** + * Get header name + * @return the header name + */ + public String getName() { + return name; + } + + /** + * Set header value + * @param name the header name + */ + public void setName(String name) { + this.name = name; + } + + /** + * Get header value + * @return the header value + */ + public String getValue() { + return value; + } + + /** + * Set header value + * @param value the header value + */ + public void setValue(String value) { + this.value = value; + } + + @Override + public String toString() { + return "HTTPHeader [name=" + name + ", value=" + value + "]"; + } +} diff --git a/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/HTTPUtils.java b/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/HTTPUtils.java new file mode 100644 index 0000000000..6ab6216522 --- /dev/null +++ b/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/HTTPUtils.java @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.common.http; + +import java.math.BigInteger; +import java.security.MessageDigest; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * + * @tuscany.spi.extension.asclient + * + */ +public class HTTPUtils { + + /** + * Utility method to set common http headers and other stuff in a + * default http response. Applications / Extensions can then override and + * tweak as they see fit. + * + * @param response + */ + public static void prepareHTTPResponse(HttpServletResponse response) { + response.setDateHeader("Date", System.currentTimeMillis()); + } + + /** + * Calculate the relative request path taking in consideration if + * the application is running in a embedded webContatiner or from + * within a web application server host environment + * + * @param request The http request + * @return the relative path + */ + public static String getRequestPath(HttpServletRequest request) { + // Get the request path + String contextPath = request.getContextPath(); + String servletPath = request.getServletPath(); + String requestURI = request.getRequestURI(); + + int contextPathLength = contextPath.length(); + int servletPathLenght = servletPath.contains(contextPath) ? servletPath.length() - contextPath.length() : servletPath.length(); + + String requestPath = requestURI.substring(contextPathLength + servletPathLenght); + + return requestPath; + } + + /** + * Calculate the context root for an application taking in consideration if + * the application is running in a embedded webContatiner or from + * within a web application server host environment. + * + * In the case of webContainer the contextRoot will always be a empty string. + * + * @param request The http request + * @return the contextRoot + */ + public static String getContextRoot(HttpServletRequest request) { + // Get the request path + String contextPath = request.getContextPath(); + String requestURI = request.getRequestURI(); + + int contextPathLength = contextPath.length(); + + String contextRoot = requestURI.substring(0, contextPathLength); + + return contextRoot; + } + + /** + * Calculate the ETag using MD5 Hash Algorithm + * @param content the content to hash + * @return + */ + public static String calculateHashETag(byte[] content) { + String eTag = "invalid"; + try { + MessageDigest messageDigest = MessageDigest.getInstance("MD5"); + byte[] digest = messageDigest.digest(content); + BigInteger number = new BigInteger(1, digest); + StringBuffer sb = new StringBuffer('0'); + sb.append(number.toString(16)); + eTag = sb.toString(); + } catch(Exception e) { + //ignore, we will return random etag + eTag = Integer.toString((new java.util.Random()).nextInt(Integer.MAX_VALUE)); + } + return eTag; + } +} diff --git a/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/ThreadHTTPContext.java b/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/ThreadHTTPContext.java new file mode 100644 index 0000000000..fb1206f005 --- /dev/null +++ b/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/ThreadHTTPContext.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.common.http; + +public class ThreadHTTPContext { + + private static final ThreadLocal<HTTPContext> CONTEXT = new ThreadLocal<HTTPContext>(); + + private ThreadHTTPContext() { + } + + public static HTTPContext setHTTPContext(HTTPContext context) { + HTTPContext old = CONTEXT.get(); + CONTEXT.set(context); + return old; + } + + public static HTTPContext getHTTPContext() { + return CONTEXT.get(); + } + + public static void removeHTTPContext() { + CONTEXT.remove(); + } +} diff --git a/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/cors/CORS.java b/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/cors/CORS.java new file mode 100644 index 0000000000..15852e24c8 --- /dev/null +++ b/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/cors/CORS.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.common.http.cors; + +/** + * @version $Rev$ $Date$ + */ +public interface CORS { + /** + * + * @return + */ + CORSConfiguration getCORSConfiguration(); + + /** + * + * @param config + */ + void setCORSConfiguration(CORSConfiguration config); +} diff --git a/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/cors/CORSConfiguration.java b/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/cors/CORSConfiguration.java new file mode 100644 index 0000000000..0d89c5c9cb --- /dev/null +++ b/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/cors/CORSConfiguration.java @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.common.http.cors; + +import java.util.ArrayList; +import java.util.List; + +public class CORSConfiguration { + List<String> allowOrigins = new ArrayList<String>(); + List<String> allowHeaders = new ArrayList<String>(); + List<String> exposeHeaders = new ArrayList<String>(); + List<String> allowMethods = new ArrayList<String>(); + boolean allowCredentials; + int maxAge; + + public CORSConfiguration() { + + } + + public List<String> getAllowOrigins() { + return allowOrigins; + } + + public void setAllowOrigins(List<String> allowOrigins) { + this.allowOrigins = allowOrigins; + } + + public List<String> getAllowHeaders() { + return allowHeaders; + } + + public void setAllowHeaders(List<String> allowHeaders) { + this.allowHeaders = allowHeaders; + } + + public List<String> getExposeHeaders() { + return exposeHeaders; + } + + public void setExposeHeaders(List<String> exposeHeaders) { + this.exposeHeaders = exposeHeaders; + } + + public List<String> getAllowMethods() { + return allowMethods; + } + + public void setAllowMethods(List<String> allowMethods) { + this.allowMethods = allowMethods; + } + + public boolean isAllowCredentials() { + return allowCredentials; + } + + public void setAllowCredentials(boolean allowCredentials) { + this.allowCredentials = allowCredentials; + } + + public int getMaxAge() { + return maxAge; + } + + public void setMaxAge(int maxAge) { + this.maxAge = maxAge; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "CORSConfiguration [allowOrigins=" + allowOrigins + + ", allowHeaders=" + + allowHeaders + + ", exposeHeaders=" + + exposeHeaders + + ", allowMethods=" + + allowMethods + + ", allowCredentials=" + + allowCredentials + + ", maxAge=" + + maxAge + + "]"; + } + + +} diff --git a/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/cors/CORSConfigurationFactory.java b/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/cors/CORSConfigurationFactory.java new file mode 100644 index 0000000000..5cbd6865a0 --- /dev/null +++ b/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/cors/CORSConfigurationFactory.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.common.http.cors; + +/** + * Factory for the CORS Configuration + */ +public class CORSConfigurationFactory { + + /** + * Create a new CORS Configuration Model + * @return + */ + public CORSConfiguration createCORSConfiguration() { + return new CORSConfiguration(); + } +} diff --git a/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/cors/CORSHeaderProcessor.java b/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/cors/CORSHeaderProcessor.java new file mode 100644 index 0000000000..ffb92f520f --- /dev/null +++ b/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/cors/CORSHeaderProcessor.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.common.http.cors; + +import java.io.IOException; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class CORSHeaderProcessor { + public static void processCORS(CORSConfiguration config, HttpServletRequest request, HttpServletResponse response) + throws IOException { + + if (config == null) { + String allowHeaders = request.getHeader("Access-Control-Request-Headers"); + if (allowHeaders == null) { + allowHeaders = "Content-Type, Accept, Origin, X-Requested-With"; + } + String allowMethods = request.getHeader("Access-Control-Request-Method"); + if (allowMethods == null) { + allowHeaders = "OPTIONS, HEAD, GET, POST, PUT, DELETE"; + } + + String allowOrigins = request.getHeader("Origin"); + if (allowOrigins == null) { + allowOrigins = "*"; + } + + response.setHeader("Access-Control-Allow-Origin", allowOrigins); + response.setHeader("Access-Control-Allow-Headers", allowHeaders); + response.setHeader("Access-Control-Allow-Credentials", "true"); + if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { + response.setHeader("Access-Control-Allow-Methods", allowMethods); + response.setHeader("Access-Control-Max-Age", "1728000"); + } + return; + } + + if (config.isAllowCredentials()) { + response.setHeader("Access-Control-Allow-Credentials", "true"); + } + + if (config.getMaxAge() > 0) { + response.setHeader("Access-Control-Max-Age", Integer.toString(config.getMaxAge())); + } + + response.setHeader("Access-Control-Allow-Origin", getAllowOrigins(config, request)); + response.setHeader("Access-Control-Allow-Methods", getAllowMethods(config)); + response.setHeader("Access-Control-Allow-Headers", getAllowHeaders(config)); + response.setHeader("Access-Control-Expose-Headers", getExposeHeaders(config)); + } + + private static String getAllowOrigins(CORSConfiguration config, HttpServletRequest request) { + String allowOrigins = request.getHeader("Origin"); + if (allowOrigins == null) { + allowOrigins = "*"; + } + return getListValues(config.getAllowOrigins(), allowOrigins); + } + + private static String getAllowMethods(CORSConfiguration config) { + return getListValues(config.getAllowMethods(), "OPTIONS, HEAD, GET, POST, PUT, DELETE"); + } + + private static String getAllowHeaders(CORSConfiguration config) { + return getListValues(config.getAllowHeaders(), "X-Requested-With, Content-Type, Accept, Origin"); + } + + private static String getExposeHeaders(CORSConfiguration config) { + return getListValues(config.getExposeHeaders(), "X-Requested-With, Content-Type"); + } + + private static String getListValues(List<String> list, String defaultValue) { + StringBuffer values = new StringBuffer(); + if (list != null && list.isEmpty() == false) { + for (String value : list) { + values.append(value).append(","); + } + values.deleteCharAt(values.length()); + } else { + values.append(defaultValue); + } + + return values.toString(); + } +} diff --git a/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/cors/xml/CORSConfigurationProcessor.java b/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/cors/xml/CORSConfigurationProcessor.java new file mode 100644 index 0000000000..bc396d66f3 --- /dev/null +++ b/sca-java-2.x/tags/2.0-RC1/modules/common-http/src/main/java/org/apache/tuscany/sca/common/http/cors/xml/CORSConfigurationProcessor.java @@ -0,0 +1,256 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.common.http.cors.xml; + +import static javax.xml.stream.XMLStreamConstants.END_ELEMENT; +import static javax.xml.stream.XMLStreamConstants.START_ELEMENT; + +import javax.xml.namespace.QName; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import javax.xml.stream.XMLStreamWriter; + +import org.apache.tuscany.sca.assembly.Base; +import org.apache.tuscany.sca.common.http.cors.CORSConfiguration; +import org.apache.tuscany.sca.common.http.cors.CORSConfigurationFactory; +import org.apache.tuscany.sca.contribution.processor.BaseStAXArtifactProcessor; +import org.apache.tuscany.sca.contribution.processor.ContributionReadException; +import org.apache.tuscany.sca.contribution.processor.ContributionResolveException; +import org.apache.tuscany.sca.contribution.processor.ContributionWriteException; +import org.apache.tuscany.sca.contribution.processor.ProcessorContext; +import org.apache.tuscany.sca.contribution.processor.StAXArtifactProcessor; +import org.apache.tuscany.sca.contribution.processor.StAXAttributeProcessor; +import org.apache.tuscany.sca.contribution.resolver.ModelResolver; +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.core.FactoryExtensionPoint; + +/** + * CORS Configuration Artifact processor + * @version $Rev$ $Date$ + */ +public class CORSConfigurationProcessor extends BaseStAXArtifactProcessor implements StAXArtifactProcessor<CORSConfiguration> { + + private static final QName CORS_QNAME = new QName(Base.SCA11_TUSCANY_NS, "corsConfiguration"); + + private static final QName ALLOW_CREDENTIALS_QNAME = new QName(Base.SCA11_TUSCANY_NS, "allowCredentials"); + private static final QName MAX_AGE_QNAME = new QName(Base.SCA11_TUSCANY_NS, "maxAge"); + + private static final QName ALLOW_ORIGINS_QNAME = new QName(Base.SCA11_TUSCANY_NS, "allowOrigins"); + private static final QName ORIGIN_QNAME = new QName(Base.SCA11_TUSCANY_NS, "origin"); + + private static final QName ALLOW_METHODS_QNAME = new QName(Base.SCA11_TUSCANY_NS, "allowMethods"); + private static final QName METHOD_QNAME = new QName(Base.SCA11_TUSCANY_NS, "method"); + + private static final QName ALLOW_HEADERS_QNAME = new QName(Base.SCA11_TUSCANY_NS, "allowHeaders"); + private static final QName EXPOSE_HEADERS_QNAME = new QName(Base.SCA11_TUSCANY_NS, "exposeHeaders"); + private static final QName HEADER_QNAME = new QName(Base.SCA11_TUSCANY_NS, "header"); + + + + private StAXArtifactProcessor<Object> extensionProcessor; + private CORSConfigurationFactory corsConfigurationFactory; + + public CORSConfigurationProcessor(ExtensionPointRegistry extensionPoints, + StAXArtifactProcessor<Object> extensionProcessor, + StAXAttributeProcessor<Object> extensionAttributeProcessor) { + + FactoryExtensionPoint modelFactories = extensionPoints.getExtensionPoint(FactoryExtensionPoint.class); + this.corsConfigurationFactory = modelFactories.getFactory(CORSConfigurationFactory.class); + + this.extensionProcessor = (StAXArtifactProcessor<Object>)extensionProcessor; + } + + + @Override + public QName getArtifactType() { + return CORS_QNAME; + } + + @Override + public Class<CORSConfiguration> getModelType() { + return CORSConfiguration.class; + } + + @Override + public CORSConfiguration read(XMLStreamReader reader, ProcessorContext context) throws ContributionReadException, XMLStreamException { + + CORSConfiguration corsConfiguration = corsConfigurationFactory.createCORSConfiguration(); + + /** + * <corsConfiguration> + * <allowCredentials>true</allowCredentials> + * + * <maxAge>100</maxAge> + * + * <allowOrigins> + * <origin>http://www.sfly.com</origin> + * </allowOrigins> + * + * <allowMethods> + * <method>PUT</method> + * <method>POST</method> + * </allowMethods> + * + * <allowHeaders> + * <header>X-custom-1</header> + * <header>X-custom-2</header> + * </allowHeaders> + * + * <exposeHeaders> + * <header>X-custom-1</header> + * <header>X-custom-2</header> + * </exposeHeaders> + * + * </corsConfiguration> + **/ + + HeaderElementType headerType = null; + + while(reader.hasNext()) { + QName elementName = null; + int event = reader.getEventType(); + switch (event) { + case START_ELEMENT: + elementName = reader.getName(); + + if (ALLOW_CREDENTIALS_QNAME.equals(elementName)) { + String value = reader.getElementText(); + corsConfiguration.setAllowCredentials(Boolean.parseBoolean(value)); + } else if (MAX_AGE_QNAME.equals(elementName)) { + String value = reader.getElementText(); + corsConfiguration.setMaxAge(Integer.parseInt(value)); + } else if (ORIGIN_QNAME.equals(elementName)) { + String value = reader.getElementText(); + corsConfiguration.getAllowOrigins().add(value); + } else if (METHOD_QNAME.equals(elementName)) { + String value = reader.getElementText(); + corsConfiguration.getAllowMethods().add(value); + } else if (ALLOW_HEADERS_QNAME.equals(elementName)) { + headerType = HeaderElementType.ALLOWHEADERS; + } else if (EXPOSE_HEADERS_QNAME.equals(elementName)) { + headerType = HeaderElementType.EXPOSEHEADERS; + } else if (HEADER_QNAME.equals(elementName)) { + if(headerType != null) { + String value = reader.getElementText(); + if(headerType == HeaderElementType.ALLOWHEADERS) { + corsConfiguration.getAllowHeaders().add(value); + } else { + corsConfiguration.getExposeHeaders().add(value); + } + } + } + + break; + + case END_ELEMENT: + elementName = reader.getName(); + if(CORS_QNAME.equals(elementName)) { + return corsConfiguration; + } + break; + } + + + + // Read the next element + if (reader.hasNext()) { + reader.next(); + } + } + + return corsConfiguration; + } + + @Override + public void write(CORSConfiguration model, XMLStreamWriter writer, ProcessorContext context) throws ContributionWriteException, XMLStreamException { + + writeStart(writer, CORS_QNAME.getNamespaceURI(), CORS_QNAME.getLocalPart()); + + // Write allowCredentials + writeStart(writer, ALLOW_CREDENTIALS_QNAME.getNamespaceURI(), ALLOW_CREDENTIALS_QNAME.getLocalPart()); + writer.writeCharacters(Boolean.toString(model.isAllowCredentials())); + writeEnd(writer); + + // Write maxAge + writeStart(writer, MAX_AGE_QNAME.getNamespaceURI(), MAX_AGE_QNAME.getLocalPart()); + writer.writeCharacters(Integer.toString(model.getMaxAge())); + writeEnd(writer); + + // Allow origins + if(model.getAllowOrigins() != null && model.getAllowOrigins().isEmpty() == false) { + writeStart(writer, ALLOW_ORIGINS_QNAME.getNamespaceURI(), ALLOW_ORIGINS_QNAME.getLocalPart()); + for(String origin : model.getAllowOrigins()) { + writeStart(writer, ORIGIN_QNAME.getNamespaceURI(), ORIGIN_QNAME.getLocalPart()); + writer.writeCharacters(origin); + writeEnd(writer); + } + writeEnd(writer); + } + + // Allow methods + if(model.getAllowMethods() != null && model.getAllowMethods().isEmpty() == false) { + writeStart(writer, ALLOW_METHODS_QNAME.getNamespaceURI(), ALLOW_METHODS_QNAME.getLocalPart()); + for(String method : model.getAllowMethods()) { + writeStart(writer, METHOD_QNAME.getNamespaceURI(), METHOD_QNAME.getLocalPart()); + writer.writeCharacters(method); + writeEnd(writer); + } + writeEnd(writer); + } + + + // Allow headers + if(model.getAllowHeaders() != null && model.getAllowHeaders().isEmpty() == false) { + writeStart(writer, ALLOW_HEADERS_QNAME.getNamespaceURI(), ALLOW_HEADERS_QNAME.getLocalPart()); + for(String header : model.getAllowHeaders()) { + writeStart(writer, HEADER_QNAME.getNamespaceURI(), HEADER_QNAME.getLocalPart()); + writer.writeCharacters(header); + writeEnd(writer); + } + writeEnd(writer); + } + + + // Exposed headers + if(model.getExposeHeaders() != null && model.getExposeHeaders().isEmpty() == false) { + writeStart(writer, EXPOSE_HEADERS_QNAME.getNamespaceURI(), EXPOSE_HEADERS_QNAME.getLocalPart()); + for(String header : model.getExposeHeaders()) { + writeStart(writer, HEADER_QNAME.getNamespaceURI(), HEADER_QNAME.getLocalPart()); + writer.writeCharacters(header); + writeEnd(writer); + } + writeEnd(writer); + } + writeEnd(writer); + } + + @Override + public void resolve(CORSConfiguration model, ModelResolver resolver, ProcessorContext context) throws ContributionResolveException { + // Should not need to do anything here for now... + } + + + enum HeaderElementType { + ALLOWHEADERS, + EXPOSEHEADERS + } + + +} |