From feec66ab73cd22445fe56322117b05c881b6267a Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Fri, 14 May 2010 19:29:44 +0000 Subject: [PATCH] SPR-7197 + removed compile time dependency on JBoss VFS + added support for JBoss AS 6.0 / JBoss VFS 3.0.0 infrastructure git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@3336 50f2f4bb-b051-0410-bef5-90022cba6387 --- org.springframework.core/ivy.xml | 1 - org.springframework.core/pom.xml | 7 - .../io/AbstractFileResolvingResource.java | 9 +- .../springframework/core/io/VfsResource.java | 82 +++--- .../org/springframework/core/io/VfsUtils.java | 246 ++++++++++++++++++ .../PathMatchingResourcePatternResolver.java | 57 ++-- .../core/io/support/VfsPatternUtils.java | 49 ++++ org.springframework.core/template.mf | 1 + 8 files changed, 372 insertions(+), 80 deletions(-) create mode 100644 org.springframework.core/src/main/java/org/springframework/core/io/VfsUtils.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/io/support/VfsPatternUtils.java diff --git a/org.springframework.core/ivy.xml b/org.springframework.core/ivy.xml index 9f1acc53c15..2a08f9c36f5 100644 --- a/org.springframework.core/ivy.xml +++ b/org.springframework.core/ivy.xml @@ -26,7 +26,6 @@ - diff --git a/org.springframework.core/pom.xml b/org.springframework.core/pom.xml index 4fb294ae46f..9aac4c2525d 100644 --- a/org.springframework.core/pom.xml +++ b/org.springframework.core/pom.xml @@ -44,13 +44,6 @@ aspectjweaver true - - org.jboss.vfs - com.springsource.org.jboss.virtual - 2.1.0.GA - compile - true - javax.servlet servlet-api diff --git a/org.springframework.core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java b/org.springframework.core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java index 0281c641281..b1ed321aa31 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java +++ b/org.springframework.core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java @@ -21,8 +21,6 @@ import java.io.IOException; import java.net.URI; import java.net.URL; -import org.jboss.virtual.VFS; - import org.springframework.util.ResourceUtils; /** @@ -89,12 +87,11 @@ public abstract class AbstractFileResolvingResource extends AbstractResource { private static class VfsResourceDelegate { public static Resource getResource(URL url) throws IOException { - return new VfsResource(VFS.getRoot(url)); + return new VfsResource(VfsUtils.getRoot(url)); } public static Resource getResource(URI uri) throws IOException { - return new VfsResource(VFS.getRoot(uri)); + return new VfsResource(VfsUtils.getRoot(uri)); } } - -} +} \ No newline at end of file diff --git a/org.springframework.core/src/main/java/org/springframework/core/io/VfsResource.java b/org.springframework.core/src/main/java/org/springframework/core/io/VfsResource.java index 8f542397989..5e487cd36cf 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/io/VfsResource.java +++ b/org.springframework.core/src/main/java/org/springframework/core/io/VfsResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,10 +22,6 @@ import java.io.InputStream; import java.net.URI; import java.net.URL; -import org.jboss.virtual.VFS; -import org.jboss.virtual.VFSUtils; -import org.jboss.virtual.VirtualFile; - import org.springframework.core.NestedIOException; import org.springframework.util.Assert; @@ -34,97 +30,83 @@ import org.springframework.util.Assert; * * @author Ales Justin * @author Juergen Hoeller + * @author Costin Leau * @since 3.0 * @see org.jboss.virtual.VirtualFile + * @see org.jboss.vfs.VirtualFile */ public class VfsResource extends AbstractResource { - private final VirtualFile file; - + private final Object resource; - public VfsResource(VirtualFile file) { - Assert.notNull(file, "VirtualFile must not be null"); - this.file = file; + public VfsResource(Object resources) { + Assert.notNull(resources, "VirtualFile must not be null"); + this.resource = resources; } - public boolean exists() { - try { - return this.file.exists(); - } - catch (IOException ex) { - return false; - } + return VfsUtils.exists(resource); } public boolean isReadable() { - try { - return (this.file.getSize() > 0); - } - catch (IOException e) { - return false; - } + return VfsUtils.isReadable(resource); } public long lastModified() throws IOException { - return this.file.getLastModified(); + return VfsUtils.getLastModified(resource); } public InputStream getInputStream() throws IOException { - return this.file.openStream(); + return VfsUtils.getInputStream(resource); } public URL getURL() throws IOException { try { - return this.file.toURL(); - } - catch (Exception ex) { - throw new NestedIOException("Failed to obtain URL for file " + this.file, ex); + return VfsUtils.getURL(resource); + } catch (Exception ex) { + throw new NestedIOException("Failed to obtain URL for file " + this.resource, ex); } } public URI getURI() throws IOException { try { - return this.file.toURI(); - } - catch (Exception ex) { - throw new NestedIOException("Failed to obtain URI for " + this.file, ex); + return VfsUtils.getURI(resource); + } catch (Exception ex) { + throw new NestedIOException("Failed to obtain URI for " + this.resource, ex); } } public File getFile() throws IOException { - if (VFSUtils.isNestedFile(this.file)) { - throw new IOException("File resolution not supported for nested resource: " + this.file); - } - try { - return new File(VFSUtils.getCompatibleURI(file)); - } - catch (Exception ex) { - throw new NestedIOException("Failed to obtain File reference for " + this.file, ex); - } + return VfsUtils.getFile(resource); } public Resource createRelative(String relativePath) throws IOException { - return new VfsResource(VFS.getRoot(new URL(getURL(), relativePath))); + if (!relativePath.startsWith(".") && relativePath.contains("/")) { + try { + return new VfsResource(VfsUtils.getChild(resource, relativePath)); + } catch (IOException ex) { + // fall back to #getRelative + } + } + + return new VfsResource(VfsUtils.getRelative(new URL(getURL(), relativePath))); } public String getFilename() { - return this.file.getName(); + return VfsUtils.getName(resource); } public String getDescription() { - return this.file.toString(); + return this.resource.toString(); } - @Override public boolean equals(Object obj) { - return (obj == this || (obj instanceof VfsResource && this.file.equals(((VfsResource) obj).file))); + return (obj == this || (obj instanceof VfsResource && this.resource.equals(((VfsResource) obj).resource))); } @Override public int hashCode() { - return this.file.hashCode(); + return this.resource.hashCode(); } - -} +} \ No newline at end of file diff --git a/org.springframework.core/src/main/java/org/springframework/core/io/VfsUtils.java b/org.springframework.core/src/main/java/org/springframework/core/io/VfsUtils.java new file mode 100644 index 00000000000..64069bd6f0f --- /dev/null +++ b/org.springframework.core/src/main/java/org/springframework/core/io/VfsUtils.java @@ -0,0 +1,246 @@ +/* + * Copyright 2010 the original author or authors. + * + * Licensed 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.springframework.core.io; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.URI; +import java.net.URL; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.core.NestedIOException; +import org.springframework.util.ReflectionUtils; + +/** + * Utility for detecting the JBoss VFS version available in the classpath. + * JBoss AS 5+ uses VFS 2.x (package org.jboss.virtual) while JBoss AS 6+ uses + * VFS 3.x (package org.jboss.vfs). + *

+ * Thanks go to Marius Bogoevici for the initial patch. + * + * @author Costin Leau + * + */ +public abstract class VfsUtils { + + private static final Log log = LogFactory.getLog(VfsUtils.class); + + private static final String VFS2_PKG = "org.jboss.virtual."; + private static final String VFS3_PKG = "org.jboss.vfs."; + private static final String VFS_NAME = "VFS"; + + private static enum VFS_VER { + V2, V3 + } + + private static VFS_VER version = null; + + private static Method VFS_METHOD_GET_ROOT_URL = null; + private static Method VFS_METHOD_GET_ROOT_URI = null; + + private static Method VIRTUAL_FILE_METHOD_EXISTS = null; + private static Method VIRTUAL_FILE_METHOD_GET_SIZE; + private static Method VIRTUAL_FILE_METHOD_GET_LAST_MODIFIED; + private static Method VIRTUAL_FILE_METHOD_GET_CHILD; + private static Method VIRTUAL_FILE_METHOD_GET_INPUT_STREAM; + private static Method VIRTUAL_FILE_METHOD_TO_URL; + private static Method VIRTUAL_FILE_METHOD_TO_URI; + private static Method VIRTUAL_FILE_METHOD_GET_NAME; + private static Method VIRTUAL_FILE_METHOD_GET_PATH_NAME; + protected static Class VIRTUAL_FILE_VISITOR_INTERFACE; + protected static Method VIRTUAL_FILE_METHOD_VISIT; + + + private static Method VFS_UTILS_METHOD_IS_NESTED_FILE = null; + private static Method VFS_UTILS_METHOD_GET_COMPATIBLE_URI = null; + private static Field VISITOR_ATTRIBUTES_FIELD_RECURSE = null; + private static Method GET_PHYSICAL_FILE = null; + + static { + ClassLoader loader = VfsUtils.class.getClassLoader(); + + String pkg = ""; + + Class vfsClass = null; + + // check JBoss 6 + try { + vfsClass = loader.loadClass(VFS3_PKG + VFS_NAME); + version = VFS_VER.V3; + pkg = VFS3_PKG; + + if (log.isDebugEnabled()) + log.debug("JBoss VFS packages for JBoss AS 6 found"); + } catch (ClassNotFoundException ex) { + // fallback to JBoss 5 + if (log.isDebugEnabled()) + log.debug("JBoss VFS packages for JBoss AS 6 not found; falling back to JBoss AS 5 packages"); + try { + vfsClass = loader.loadClass(VFS2_PKG + VFS_NAME); + + version = VFS_VER.V2; + pkg = VFS2_PKG; + + if (log.isDebugEnabled()) + log.debug("JBoss VFS packages for JBoss AS 5 found"); + } catch (ClassNotFoundException ex1) { + log.error("JBoss VFS packages (for both JBoss AS 5 and 6) were not found - JBoss VFS support disabled"); + throw new IllegalStateException("Cannot detect JBoss VFS packages", ex1); + } + } + + // cache reflective information + try { + String methodName = (VFS_VER.V3.equals(version) ? "getChild" : "getRoot"); + + VFS_METHOD_GET_ROOT_URL = ReflectionUtils.findMethod(vfsClass, methodName, URL.class); + VFS_METHOD_GET_ROOT_URI = ReflectionUtils.findMethod(vfsClass, methodName, URI.class); + + Class virtualFile = loader.loadClass(pkg + "VirtualFile"); + + VIRTUAL_FILE_METHOD_EXISTS = ReflectionUtils.findMethod(virtualFile, "exists"); + VIRTUAL_FILE_METHOD_GET_SIZE = ReflectionUtils.findMethod(virtualFile, "getSize"); + VIRTUAL_FILE_METHOD_GET_INPUT_STREAM = ReflectionUtils.findMethod(virtualFile, "openStream"); + VIRTUAL_FILE_METHOD_GET_LAST_MODIFIED = ReflectionUtils.findMethod(virtualFile, "getLastModified"); + VIRTUAL_FILE_METHOD_TO_URI = ReflectionUtils.findMethod(virtualFile, "toURI"); + VIRTUAL_FILE_METHOD_TO_URL = ReflectionUtils.findMethod(virtualFile, "toURL"); + VIRTUAL_FILE_METHOD_GET_NAME = ReflectionUtils.findMethod(virtualFile, "getName"); + VIRTUAL_FILE_METHOD_GET_PATH_NAME = ReflectionUtils.findMethod(virtualFile, "getPathName"); + GET_PHYSICAL_FILE = ReflectionUtils.findMethod(virtualFile, "getPhysicalFile"); + + methodName = (VFS_VER.V3.equals(version) ? "getChild" : "findChild"); + + VIRTUAL_FILE_METHOD_GET_CHILD = ReflectionUtils.findMethod(virtualFile, methodName, String.class); + + Class utilsClass = loader.loadClass(pkg + "VFSUtils"); + + VFS_UTILS_METHOD_GET_COMPATIBLE_URI = ReflectionUtils.findMethod(utilsClass, "getCompatibleURI", + virtualFile); + VFS_UTILS_METHOD_IS_NESTED_FILE = ReflectionUtils.findMethod(utilsClass, "isNestedFile", virtualFile); + + VIRTUAL_FILE_VISITOR_INTERFACE = loader.loadClass(pkg + "VirtualFileVisitor"); + VIRTUAL_FILE_METHOD_VISIT = ReflectionUtils.findMethod(virtualFile, "visit", VIRTUAL_FILE_VISITOR_INTERFACE); + + Class visitorAttributesClass = loader.loadClass(pkg + "VisitorAttributes"); + VISITOR_ATTRIBUTES_FIELD_RECURSE = ReflectionUtils.findField(visitorAttributesClass, "RECURSE"); + + } catch (ClassNotFoundException ex) { + throw new IllegalStateException("Could not detect the JBoss VFS infrastructure", ex); + } + } + + protected static Object invokeVfsMethod(Method method, Object target, Object... args) throws IOException { + try { + return method.invoke(target, args); + } catch (InvocationTargetException ex) { + Throwable targetEx = ex.getTargetException(); + if (targetEx instanceof IOException) { + throw (IOException) targetEx; + } + ReflectionUtils.handleInvocationTargetException(ex); + } catch (Exception ex) { + ReflectionUtils.handleReflectionException(ex); + } + + throw new IllegalStateException("Invalid code path reached"); + } + + static boolean exists(Object vfsResource) { + try { + return (Boolean) invokeVfsMethod(VIRTUAL_FILE_METHOD_EXISTS, vfsResource); + } catch (IOException ex) { + return false; + } + } + + static boolean isReadable(Object vfsResource) { + try { + return ((Long) invokeVfsMethod(VIRTUAL_FILE_METHOD_GET_SIZE, vfsResource) > 0); + } catch (IOException ex) { + return false; + } + } + + static long getLastModified(Object vfsResource) throws IOException { + return (Long) invokeVfsMethod(VIRTUAL_FILE_METHOD_GET_LAST_MODIFIED, vfsResource); + } + + static InputStream getInputStream(Object vfsResource) throws IOException { + return (InputStream) invokeVfsMethod(VIRTUAL_FILE_METHOD_GET_INPUT_STREAM, vfsResource); + } + + static URL getURL(Object vfsResource) throws IOException { + return (URL) invokeVfsMethod(VIRTUAL_FILE_METHOD_TO_URL, vfsResource); + } + + static URI getURI(Object vfsResource) throws IOException { + return (URI) invokeVfsMethod(VIRTUAL_FILE_METHOD_TO_URI, vfsResource); + } + + static String getName(Object vfsResource) { + try { + return (String) invokeVfsMethod(VIRTUAL_FILE_METHOD_GET_NAME, vfsResource); + } catch (IOException ex) { + throw new IllegalStateException("Cannot get resource name", ex); + } + } + + static Object getRelative(URL url) throws IOException { + return invokeVfsMethod(VFS_METHOD_GET_ROOT_URL, null, url); + } + + static Object getChild(Object vfsResource, String path) throws IOException { + return invokeVfsMethod(VIRTUAL_FILE_METHOD_GET_CHILD, vfsResource, path); + } + + static File getFile(Object vfsResource) throws IOException { + if (VFS_VER.V2.equals(version)) { + if ((Boolean) invokeVfsMethod(VFS_UTILS_METHOD_IS_NESTED_FILE, null, vfsResource)) { + throw new IOException("File resolution not supported for nested resource: " + vfsResource); + } + try { + return new File((URI) invokeVfsMethod(VFS_UTILS_METHOD_GET_COMPATIBLE_URI, null, vfsResource)); + } catch (Exception ex) { + throw new NestedIOException("Failed to obtain File reference for " + vfsResource, ex); + } + } + else { + return (File) invokeVfsMethod(GET_PHYSICAL_FILE, vfsResource); + } + } + + protected static Object getRoot(URL url) throws IOException { + return invokeVfsMethod(VFS_METHOD_GET_ROOT_URL, null, url); + } + + static Object getRoot(URI url) throws IOException { + return invokeVfsMethod(VFS_METHOD_GET_ROOT_URI, null, url); + } + + protected static Object doGetVisitorAttribute() { + return ReflectionUtils.getField(VISITOR_ATTRIBUTES_FIELD_RECURSE, null); + } + + protected static String doGetPath(Object resource) { + return (String) ReflectionUtils.invokeMethod(VIRTUAL_FILE_METHOD_GET_PATH_NAME, resource); + } +} \ No newline at end of file diff --git a/org.springframework.core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java b/org.springframework.core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java index 0d04f3b01f7..aaad408872c 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java +++ b/org.springframework.core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java @@ -18,6 +18,7 @@ package org.springframework.core.io.support; import java.io.File; import java.io.IOException; +import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.net.JarURLConnection; import java.net.URISyntaxException; @@ -32,11 +33,6 @@ import java.util.jar.JarFile; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.jboss.virtual.VFS; -import org.jboss.virtual.VirtualFile; -import org.jboss.virtual.VirtualFileVisitor; -import org.jboss.virtual.VisitorAttributes; - import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; @@ -160,6 +156,7 @@ import org.springframework.util.StringUtils; * @author Juergen Hoeller * @author Colin Sampaleanu * @author Marius Bogoevici + * @author Costin Leau * @since 1.0.2 * @see #CLASSPATH_ALL_URL_PREFIX * @see org.springframework.util.AntPathMatcher @@ -649,9 +646,10 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol private static class VfsResourceMatchingDelegate { public static Set findMatchingResources(Resource rootResource, String locationPattern, PathMatcher pathMatcher) throws IOException { - VirtualFile root = VFS.getRoot(rootResource.getURL()); - PatternVirtualFileVisitor visitor = new PatternVirtualFileVisitor(root.getPathName(), locationPattern, pathMatcher); - root.visit(visitor); + Object root = VfsPatternUtils.findRoot(rootResource.getURL()); + PatternVirtualFileVisitor visitor = new PatternVirtualFileVisitor(VfsPatternUtils.getPath(root), + locationPattern, pathMatcher); + VfsPatternUtils.visit(root, visitor); return visitor.getResources(); } } @@ -660,7 +658,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol /** * VFS visitor for path matching purposes. */ - private static class PatternVirtualFileVisitor implements VirtualFileVisitor { + private static class PatternVirtualFileVisitor implements InvocationHandler { private final String subPattern; @@ -676,16 +674,44 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol this.rootPath = (rootPath.length() == 0 || rootPath.endsWith("/") ? rootPath : rootPath + "/"); } - public VisitorAttributes getAttributes() { - return VisitorAttributes.RECURSE; + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + String methodName = method.getName(); + if (Object.class.equals(method.getDeclaringClass())) { + if (methodName.equals("equals")) { + // Only consider equal when proxies are identical. + return (proxy == args[0]); + } + else if (methodName.equals("hashCode")) { + return System.identityHashCode(proxy); + } + } + else if ("getAttributes".equals(methodName)) { + return getAttributes(); + } + else if ("visit".equals(methodName)) { + visit(args[0]); + return null; + } + else if ("toString".equals(methodName)) { + return toString(); + } + + throw new IllegalStateException("Unexpected method invocation: " + method); } - public void visit(VirtualFile vf) { - if (this.pathMatcher.match(this.subPattern, vf.getPathName().substring(this.rootPath.length()))) { - this.resources.add(new VfsResource(vf)); + public void visit(Object vfsResource) { + if (this.pathMatcher.match(this.subPattern, VfsPatternUtils.getPath(vfsResource).substring( + this.rootPath.length()))) { + this.resources.add(new VfsResource(vfsResource)); } } + public Object getAttributes() { + return VfsPatternUtils.getVisitorAttribute(); + } + + public Set getResources() { return this.resources; } @@ -701,5 +727,4 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol return sb.toString(); } } - -} +} \ No newline at end of file diff --git a/org.springframework.core/src/main/java/org/springframework/core/io/support/VfsPatternUtils.java b/org.springframework.core/src/main/java/org/springframework/core/io/support/VfsPatternUtils.java new file mode 100644 index 00000000000..59924b698a2 --- /dev/null +++ b/org.springframework.core/src/main/java/org/springframework/core/io/support/VfsPatternUtils.java @@ -0,0 +1,49 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed 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.springframework.core.io.support; + +import java.io.IOException; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; +import java.net.URL; + +import org.springframework.core.io.VfsUtils; + +/** + * @author Costin Leau + * + */ +abstract class VfsPatternUtils extends VfsUtils { + static Object getVisitorAttribute() { + return doGetVisitorAttribute(); + } + + static String getPath(Object resource) { + return doGetPath(resource); + } + + static Object findRoot(URL url) throws IOException { + return getRoot(url); + } + + static void visit(Object resource, InvocationHandler visitor) throws IOException { + Object visitorProxy = Proxy.newProxyInstance(VIRTUAL_FILE_VISITOR_INTERFACE.getClassLoader(), + new Class[] { VIRTUAL_FILE_VISITOR_INTERFACE }, visitor); + + invokeVfsMethod(VIRTUAL_FILE_METHOD_VISIT, resource, visitorProxy); + } +} diff --git a/org.springframework.core/template.mf b/org.springframework.core/template.mf index ca1bb01cce0..f322fe264b3 100644 --- a/org.springframework.core/template.mf +++ b/org.springframework.core/template.mf @@ -11,6 +11,7 @@ Import-Template: org.apache.log4j.*;version="[1.2.15, 2.0.0)";resolution:=optional, org.aspectj.*;version=${aj.osgi.range};resolution:=optional, org.jboss.virtual.*;version="[2.1.0.GA, 3.0.0)";resolution:=optional, + org.jboss.vfs.*;version="[3.0.0, 4.0.0)";resolution:=optional, org.xml.sax.*;version="0";resolution:=optional, org.w3c.dom.*;version="0";resolution:=optional Ignored-Existing-Headers: