Willem what's all the code in FileUtil. At first glance this doesn't look to good, and easy to support such code.
I would like to find a better solution. And why is this only related to validator component? Can we look at another solution that doesn't require adding a lot of weird code? On Fri, Dec 21, 2012 at 3:18 AM, <ningji...@apache.org> wrote: > Author: ningjiang > Date: Fri Dec 21 02:18:34 2012 > New Revision: 1424788 > > URL: http://svn.apache.org/viewvc?rev=1424788&view=rev > Log: > CAMEL-5837 fix the schema validator imports with relative path issue > > Added: > > camel/trunk/camel-core/src/test/java/org/apache/camel/component/validator/ValidatorIncludeRelativeRouteTest.java > > camel/trunk/camel-core/src/test/resources/org/apache/camel/component/validator/xsds/ > > camel/trunk/camel-core/src/test/resources/org/apache/camel/component/validator/xsds/health/ > > camel/trunk/camel-core/src/test/resources/org/apache/camel/component/validator/xsds/health/common/ > > camel/trunk/camel-core/src/test/resources/org/apache/camel/component/validator/xsds/health/common/common.xsd > > camel/trunk/camel-core/src/test/resources/org/apache/camel/component/validator/xsds/health/health.xsd > > camel/trunk/camel-core/src/test/resources/org/apache/camel/component/validator/xsds/person.xsd > Modified: > > camel/trunk/camel-core/src/main/java/org/apache/camel/component/validator/DefaultLSResourceResolver.java > camel/trunk/camel-core/src/main/java/org/apache/camel/util/FileUtil.java > > Modified: > camel/trunk/camel-core/src/main/java/org/apache/camel/component/validator/DefaultLSResourceResolver.java > URL: > http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/component/validator/DefaultLSResourceResolver.java?rev=1424788&r1=1424787&r2=1424788&view=diff > ============================================================================== > --- > camel/trunk/camel-core/src/main/java/org/apache/camel/component/validator/DefaultLSResourceResolver.java > (original) > +++ > camel/trunk/camel-core/src/main/java/org/apache/camel/component/validator/DefaultLSResourceResolver.java > Fri Dec 21 02:18:34 2012 > @@ -50,6 +50,7 @@ public class DefaultLSResourceResolver i > throw new IllegalArgumentException(String.format("Resource: %s > refers an invalid resource without SystemId." > + " Invalid resource has type: %s, namespaceURI: %s, > publicId: %s, systemId: %s, baseURI: %s", resourceUri, type, namespaceURI, > publicId, systemId, baseURI)); > } > + > return new DefaultLSInput(publicId, systemId, baseURI); > } > > @@ -64,11 +65,47 @@ public class DefaultLSResourceResolver i > this.publicId = publicId; > this.systemId = systemId; > this.baseURI = baseURI; > - > + this.uri = getInputUri(); > + } > + > + > + private String getInputUri() { > + // find the xsd with relative path > + if (ObjectHelper.isNotEmpty(baseURI)) { > + String inputUri = getUri(getRelativePath(baseURI)); > + try { > + > ResourceHelper.resolveMandatoryResourceAsInputStream(camelContext.getClassResolver(), > inputUri); > + return inputUri; > + } catch (IOException e) { > + // ignore the exception > + } > + } > + // don't use the relative path > + return getUri(""); > + } > + > + private String getRelativePath(String base) { > + String userDir = ""; > + String answer = ""; > + if (ObjectHelper.isNotEmpty(base)) { > + try { > + userDir = FileUtil.getUserDir().toString(); > + } catch (Exception ex) { > + // do nothing here > + } > + // get the relative path from the userdir > + if (ObjectHelper.isNotEmpty(base) && base.startsWith("file") > && base.startsWith(userDir)) { > + answer = > FileUtil.onlyPath(base.substring(userDir.length())) + "/"; > + } > + } > + return answer; > + } > + > + private String getUri(String relativePath) { > if (resourcePath != null) { > - uri = resourcePath + "/" + systemId; > + return FileUtil.onlyPath(resourceUri) + "/" + relativePath + > systemId; > } else { > - uri = systemId; > + return relativePath + systemId; > } > } > > @@ -164,4 +201,6 @@ public class DefaultLSResourceResolver i > } > } > > + > + > } > > Modified: > camel/trunk/camel-core/src/main/java/org/apache/camel/util/FileUtil.java > URL: > http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/util/FileUtil.java?rev=1424788&r1=1424787&r2=1424788&view=diff > ============================================================================== > --- camel/trunk/camel-core/src/main/java/org/apache/camel/util/FileUtil.java > (original) > +++ camel/trunk/camel-core/src/main/java/org/apache/camel/util/FileUtil.java > Fri Dec 21 02:18:34 2012 > @@ -16,10 +16,13 @@ > */ > package org.apache.camel.util; > > + > import java.io.File; > import java.io.FileInputStream; > import java.io.FileOutputStream; > import java.io.IOException; > +import java.net.URI; > +import java.net.URISyntaxException; > import java.nio.channels.FileChannel; > import java.util.Iterator; > import java.util.Locale; > @@ -39,8 +42,149 @@ public final class FileUtil { > private static final transient Logger LOG = > LoggerFactory.getLogger(FileUtil.class); > private static final int RETRY_SLEEP_MILLIS = 10; > private static File defaultTempDir; > - > + > + // current value of the "user.dir" property > + private static String gUserDir; > + // cached URI object for the current value of the escaped "user.dir" > property stored as a URI > + private static URI gUserDirURI; > + // which ASCII characters need to be escaped > + private static boolean gNeedEscaping[] = new boolean[128]; > + // the first hex character if a character needs to be escaped > + private static char gAfterEscaping1[] = new char[128]; > + // the second hex character if a character needs to be escaped > + private static char gAfterEscaping2[] = new char[128]; > + private static char[] gHexChs = {'0', '1', '2', '3', '4', '5', '6', '7', > + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; > + // initialize the above 3 arrays > + static { > + for (int i = 0; i <= 0x1f; i++) { > + gNeedEscaping[i] = true; > + gAfterEscaping1[i] = gHexChs[i >> 4]; > + gAfterEscaping2[i] = gHexChs[i & 0xf]; > + } > + gNeedEscaping[0x7f] = true; > + gAfterEscaping1[0x7f] = '7'; > + gAfterEscaping2[0x7f] = 'F'; > + char[] escChs = {' ', '<', '>', '#', '%', '"', '{', '}', > + '|', '\\', '^', '~', '[', ']', '`'}; > + int len = escChs.length; > + char ch; > + for (int i = 0; i < len; i++) { > + ch = escChs[i]; > + gNeedEscaping[ch] = true; > + gAfterEscaping1[ch] = gHexChs[ch >> 4]; > + gAfterEscaping2[ch] = gHexChs[ch & 0xf]; > + } > + } > + > private FileUtil() { > + // Utils method > + } > + > + > + // To escape the "user.dir" system property, by using %HH to represent > + // special ASCII characters: 0x00~0x1F, 0x7F, ' ', '<', '>', '#', '%' > + // and '"'. It's a static method, so needs to be synchronized. > + // this method looks heavy, but since the system property isn't expected > + // to change often, so in most cases, we only need to return the URI > + // that was escaped before. > + // According to the URI spec, non-ASCII characters (whose value >= 128) > + // need to be escaped too. > + // REVISIT: don't know how to escape non-ASCII characters, especially > + // which encoding to use. Leave them for now. > + public static synchronized URI getUserDir() throws URISyntaxException { > + // get the user.dir property > + String userDir = ""; > + try { > + userDir = System.getProperty("user.dir"); > + } catch (SecurityException se) { > + } > + > + // return empty string if property value is empty string. > + if (userDir.length() == 0) { > + return new URI("file", "", "", null, null); > + } > + // compute the new escaped value if the new property value doesn't > + // match the previous one > + if (gUserDirURI != null && userDir.equals(gUserDir)) { > + return gUserDirURI; > + } > + > + // record the new value as the global property value > + gUserDir = userDir; > + > + char separator = java.io.File.separatorChar; > + userDir = userDir.replace(separator, '/'); > + > + int len = userDir.length(); > + int ch; > + StringBuffer buffer = new StringBuffer(len * 3); > + // change C:/blah to /C:/blah > + if (len >= 2 && userDir.charAt(1) == ':') { > + ch = Character.toUpperCase(userDir.charAt(0)); > + if (ch >= 'A' && ch <= 'Z') { > + buffer.append('/'); > + } > + } > + > + // for each character in the path > + int i = 0; > + for (; i < len; i++) { > + ch = userDir.charAt(i); > + // if it's not an ASCII character, break here, and use UTF-8 > encoding > + if (ch >= 128) { > + break; > + } > + if (gNeedEscaping[ch]) { > + buffer.append('%'); > + buffer.append(gAfterEscaping1[ch]); > + buffer.append(gAfterEscaping2[ch]); > + // record the fact that it's escaped > + } else { > + buffer.append((char)ch); > + } > + } > + > + // we saw some non-ascii character > + if (i < len) { > + // get UTF-8 bytes for the remaining sub-string > + byte[] bytes = null; > + byte b; > + try { > + bytes = userDir.substring(i).getBytes("UTF-8"); > + } catch (java.io.UnsupportedEncodingException e) { > + // should never happen > + return new URI("file", "", userDir, null, null); > + } > + len = bytes.length; > + > + // for each byte > + for (i = 0; i < len; i++) { > + b = bytes[i]; > + // for non-ascii character: make it positive, then escape > + if (b < 0) { > + ch = b + 256; > + buffer.append('%'); > + buffer.append(gHexChs[ch >> 4]); > + buffer.append(gHexChs[ch & 0xf]); > + } else if (gNeedEscaping[b]) { > + buffer.append('%'); > + buffer.append(gAfterEscaping1[b]); > + buffer.append(gAfterEscaping2[b]); > + } else { > + buffer.append((char)b); > + } > + } > + } > + > + // change blah/blah to blah/blah/ > + if (!userDir.endsWith("/")) { > + buffer.append('/'); > + } > + > + gUserDirURI = new URI("file", "", buffer.toString(), null, null); > + > + return gUserDirURI; > } > > /** > > Added: > camel/trunk/camel-core/src/test/java/org/apache/camel/component/validator/ValidatorIncludeRelativeRouteTest.java > URL: > http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/component/validator/ValidatorIncludeRelativeRouteTest.java?rev=1424788&view=auto > ============================================================================== > --- > camel/trunk/camel-core/src/test/java/org/apache/camel/component/validator/ValidatorIncludeRelativeRouteTest.java > (added) > +++ > camel/trunk/camel-core/src/test/java/org/apache/camel/component/validator/ValidatorIncludeRelativeRouteTest.java > Fri Dec 21 02:18:34 2012 > @@ -0,0 +1,66 @@ > +/** > + * 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.camel.component.validator; > + > +import org.apache.camel.ValidationException; > +import org.apache.camel.builder.RouteBuilder; > +import org.apache.camel.component.mock.MockEndpoint; > + > +public class ValidatorIncludeRelativeRouteTest extends > ValidatorIncludeRouteTest { > + > + public void testValidMessage() throws Exception { > + validEndpoint.expectedMessageCount(1); > + finallyEndpoint.expectedMessageCount(1); > + > + String body = "<p:person user=\"james\" xmlns:p=\"org.person\" > xmlns:h=\"org.health.check.person\" xmlns:c=\"org.health.check.common\">\n" > + + " <p:firstName>James</p:firstName>\n" > + + " <p:lastName>Strachan</p:lastName>\n" > + + " <p:city>London</p:city>\n" > + + " <h:health>\n" > + + " <h:lastCheck>2011-12-23</h:lastCheck>\n" > + + " <h:status>OK</h:status>\n" > + + " <c:commonElement>" > + + " <c:element1/>" > + + " <c:element2/>" > + + " </c:commonElement>" > + + " </h:health>\n" > + + "</p:person>"; > + > + template.sendBody("direct:start", body); > + > + MockEndpoint.assertIsSatisfied(validEndpoint, invalidEndpoint, > finallyEndpoint); > + } > + > + @Override > + protected RouteBuilder createRouteBuilder() throws Exception { > + return new RouteBuilder() { > + @Override > + public void configure() throws Exception { > + from("direct:start") > + .doTry() > + > .to("validator:org/apache/camel/component/validator/xsds/person.xsd") > + .to("mock:valid") > + .doCatch(ValidationException.class) > + .to("mock:invalid") > + .doFinally() > + .to("mock:finally") > + .end(); > + } > + }; > + } > + > +} > > Added: > camel/trunk/camel-core/src/test/resources/org/apache/camel/component/validator/xsds/health/common/common.xsd > URL: > http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/resources/org/apache/camel/component/validator/xsds/health/common/common.xsd?rev=1424788&view=auto > ============================================================================== > --- > camel/trunk/camel-core/src/test/resources/org/apache/camel/component/validator/xsds/health/common/common.xsd > (added) > +++ > camel/trunk/camel-core/src/test/resources/org/apache/camel/component/validator/xsds/health/common/common.xsd > Fri Dec 21 02:18:34 2012 > @@ -0,0 +1,14 @@ > +<?xml version="1.0" encoding="UTF-8"?> > +<xs:schema > + attributeFormDefault="unqualified" > + elementFormDefault="qualified" > + targetNamespace="org.health.check.common" > + xmlns:xs="http://www.w3.org/2001/XMLSchema"> > + <xs:element name="commonElement" type="common:commonType" > xmlns:common="org.health.check.common"/> > + <xs:complexType name="commonType"> > + <xs:sequence> > + <xs:element type="xs:string" name="element1"/> > + <xs:element type="xs:string" name="element2"/> > + </xs:sequence> > + </xs:complexType> > +</xs:schema> > \ No newline at end of file > > Added: > camel/trunk/camel-core/src/test/resources/org/apache/camel/component/validator/xsds/health/health.xsd > URL: > http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/resources/org/apache/camel/component/validator/xsds/health/health.xsd?rev=1424788&view=auto > ============================================================================== > --- > camel/trunk/camel-core/src/test/resources/org/apache/camel/component/validator/xsds/health/health.xsd > (added) > +++ > camel/trunk/camel-core/src/test/resources/org/apache/camel/component/validator/xsds/health/health.xsd > Fri Dec 21 02:18:34 2012 > @@ -0,0 +1,18 @@ > +<?xml version="1.0" encoding="UTF-8"?> > +<xs:schema > + attributeFormDefault="unqualified" > + elementFormDefault="qualified" > + targetNamespace="org.health.check.person" > + xmlns:xs="http://www.w3.org/2001/XMLSchema" > + xmlns:common="org.health.check.common"> > + <xs:import schemaLocation="common/common.xsd" > namespace="org.health.check.common"/> > + <xs:element name="health" type="org:healthType" > xmlns:org="org.health.check.person"/> > + > + <xs:complexType name="healthType"> > + <xs:sequence> > + <xs:element type="xs:string" name="lastCheck"/> > + <xs:element type="xs:string" name="status"/> > + <xs:element ref="common:commonElement" maxOccurs="1" > minOccurs="0"/> > + </xs:sequence> > + </xs:complexType> > +</xs:schema> > \ No newline at end of file > > Added: > camel/trunk/camel-core/src/test/resources/org/apache/camel/component/validator/xsds/person.xsd > URL: > http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/resources/org/apache/camel/component/validator/xsds/person.xsd?rev=1424788&view=auto > ============================================================================== > --- > camel/trunk/camel-core/src/test/resources/org/apache/camel/component/validator/xsds/person.xsd > (added) > +++ > camel/trunk/camel-core/src/test/resources/org/apache/camel/component/validator/xsds/person.xsd > Fri Dec 21 02:18:34 2012 > @@ -0,0 +1,20 @@ > +<?xml version="1.0" encoding="UTF-8"?> > +<xs:schema attributeFormDefault="unqualified" > + elementFormDefault="qualified" > + xmlns:xs="http://www.w3.org/2001/XMLSchema" > + xmlns:p="org.person" > + targetNamespace="org.person" > + xmlns:h="org.health.check.person"> > + <xs:import schemaLocation="health/health.xsd" > namespace="org.health.check.person"/> > + <xs:element name="person" type="p:personType"> > +</xs:element> > +<xs:complexType name="personType"> > + <xs:sequence> > + <xs:element type="xs:string" name="firstName"/> > + <xs:element type="xs:string" name="lastName"/> > + <xs:element type="xs:string" name="city"/> > + <xs:element ref="h:health" maxOccurs="1" minOccurs="0"/> > + </xs:sequence> > + <xs:attribute type="xs:string" name="user"/> > +</xs:complexType> > +</xs:schema> > \ No newline at end of file > > -- Claus Ibsen ----------------- Red Hat, Inc. FuseSource is now part of Red Hat Email: cib...@redhat.com Web: http://fusesource.com Twitter: davsclaus Blog: http://davsclaus.com Author of Camel in Action: http://www.manning.com/ibsen