You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by lu...@apache.org on 2012/07/05 10:49:50 UTC
svn commit: r1357494 - in /myfaces/core/trunk:
impl/src/main/java/org/apache/myfaces/config/
impl/src/main/java/org/apache/myfaces/config/annotation/
impl/src/main/java/org/apache/myfaces/config/util/
impl/src/main/java/org/apache/myfaces/spi/ impl/src...
Author: lu4242
Date: Thu Jul 5 08:49:50 2012
New Revision: 1357494
URL: http://svn.apache.org/viewvc?rev=1357494&view=rev
Log:
MYFACES-3553 [GAE] Add param to select jar files to be scanned for .faces-config.xml or .taglib.xml or annotations
Added:
myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/util/GAEUtils.java
myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/util/JarUtils.java
Modified:
myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/DefaultFacesConfigResourceProvider.java
myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/annotation/DefaultAnnotationProvider.java
myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/spi/AnnotationProvider.java
myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/spi/AnnotationProviderWrapper.java
myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/compiler/DefaultFaceletConfigResourceProvider.java
myfaces/core/trunk/shared/src/main/java/org/apache/myfaces/shared/config/MyfacesConfig.java
Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/DefaultFacesConfigResourceProvider.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/DefaultFacesConfigResourceProvider.java?rev=1357494&r1=1357493&r2=1357494&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/DefaultFacesConfigResourceProvider.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/DefaultFacesConfigResourceProvider.java Thu Jul 5 08:49:50 2012
@@ -26,9 +26,12 @@ import java.util.Enumeration;
import java.util.List;
import javax.faces.context.ExternalContext;
+import org.apache.myfaces.shared.config.MyfacesConfig;
import org.apache.myfaces.shared.util.ClassUtils;
import org.apache.myfaces.spi.FacesConfigResourceProvider;
+import org.apache.myfaces.util.ContainerUtils;
+import org.apache.myfaces.config.util.GAEUtils;
import org.apache.myfaces.view.facelets.util.Classpath;
/**
@@ -68,11 +71,27 @@ public class DefaultFacesConfigResourceP
urlSet.add(resources.nextElement());
}
- //Scan files inside META-INF ending with .faces-config.xml
- URL[] urls = Classpath.search(getClassLoader(), META_INF_PREFIX, FACES_CONFIG_SUFFIX);
- for (int i = 0; i < urls.length; i++)
+ String jarFilesToScanParam = MyfacesConfig.getCurrentInstance(context).getGaeJsfJarFiles();
+ jarFilesToScanParam = jarFilesToScanParam != null ? jarFilesToScanParam.trim() : null;
+ if (ContainerUtils.isRunningOnGoogleAppEngine(context) &&
+ jarFilesToScanParam != null &&
+ jarFilesToScanParam.length() > 0)
{
- urlSet.add(urls[i]);
+ Collection<URL> urlsGAE = GAEUtils.searchInWebLib(
+ context, getClassLoader(), jarFilesToScanParam, META_INF_PREFIX, FACES_CONFIG_SUFFIX);
+ if (urlsGAE != null)
+ {
+ urlSet.addAll(urlsGAE);
+ }
+ }
+ else
+ {
+ //Scan files inside META-INF ending with .faces-config.xml
+ URL[] urls = Classpath.search(getClassLoader(), META_INF_PREFIX, FACES_CONFIG_SUFFIX);
+ for (int i = 0; i < urls.length; i++)
+ {
+ urlSet.add(urls[i]);
+ }
}
return urlSet;
Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/annotation/DefaultAnnotationProvider.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/annotation/DefaultAnnotationProvider.java?rev=1357494&r1=1357493&r2=1357494&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/annotation/DefaultAnnotationProvider.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/annotation/DefaultAnnotationProvider.java Thu Jul 5 08:49:50 2012
@@ -50,9 +50,14 @@ import javax.faces.render.FacesRenderer;
import javax.faces.validator.FacesValidator;
import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
+import org.apache.myfaces.shared.config.MyfacesConfig;
import org.apache.myfaces.shared.util.ClassUtils;
import org.apache.myfaces.spi.AnnotationProvider;
import org.apache.myfaces.spi.AnnotationProviderFactory;
+import org.apache.myfaces.util.ContainerUtils;
+import org.apache.myfaces.config.util.GAEUtils;
+import org.apache.myfaces.config.util.JarUtils;
+import org.apache.myfaces.shared.util.StringUtils;
import org.apache.myfaces.view.facelets.util.Classpath;
/**
@@ -164,15 +169,29 @@ public class DefaultAnnotationProvider e
}
//2. Scan for annotations on classpath
- try
- {
- AnnotationProvider provider
- = AnnotationProviderFactory.getAnnotationProviderFactory(ctx).getAnnotationProvider(ctx);
- classes = getAnnotatedMetaInfClasses(ctx, provider.getBaseUrls());
+ String jarAnnotationFilesToScanParam = MyfacesConfig.getCurrentInstance(ctx).getGaeJsfAnnotationsJarFiles();
+ jarAnnotationFilesToScanParam = jarAnnotationFilesToScanParam != null ?
+ jarAnnotationFilesToScanParam.trim() : null;
+ if (ContainerUtils.isRunningOnGoogleAppEngine(ctx) &&
+ jarAnnotationFilesToScanParam != null &&
+ jarAnnotationFilesToScanParam.length() > 0)
+ {
+ // Skip call AnnotationProvider.getBaseUrls(ctx), and instead use the value of the config parameter
+ // to find which classes needs to be scanned for annotations
+ classes = getGAEAnnotatedMetaInfClasses(ctx, jarAnnotationFilesToScanParam);
}
- catch (IOException e)
+ else
{
- throw new FacesException(e);
+ try
+ {
+ AnnotationProvider provider
+ = AnnotationProviderFactory.getAnnotationProviderFactory(ctx).getAnnotationProvider(ctx);
+ classes = getAnnotatedMetaInfClasses(ctx, provider.getBaseUrls(ctx));
+ }
+ catch (IOException e)
+ {
+ throw new FacesException(e);
+ }
}
for (Class<?> clazz : classes)
@@ -183,16 +202,18 @@ public class DefaultAnnotationProvider e
//3. Scan on myfaces-impl for annotations available on myfaces-impl.
//Also scan jar including META-INF/standard-faces-config.xml
//(myfaces-impl jar file)
- URL url = getClassLoader().getResource(STANDARD_FACES_CONFIG_RESOURCE);
- if (url == null)
- {
- url = getClass().getClassLoader().getResource(STANDARD_FACES_CONFIG_RESOURCE);
- }
- classes = getAnnotatedMyfacesImplClasses(ctx, url);
- for (Class<?> clazz : classes)
- {
- processClass(map, clazz);
- }
+ // -= Leonardo Uribe =- No annotations in MyFaces jars, code not
+ // necessary, because all config is already in standard-faces-config.xml
+ //URL url = getClassLoader().getResource(STANDARD_FACES_CONFIG_RESOURCE);
+ //if (url == null)
+ //{
+ // url = getClass().getClassLoader().getResource(STANDARD_FACES_CONFIG_RESOURCE);
+ //}
+ //classes = getAnnotatedMyfacesImplClasses(ctx, url);
+ //for (Class<?> clazz : classes)
+ //{
+ // processClass(map, clazz);
+ //}
return map;
}
@@ -219,6 +240,39 @@ public class DefaultAnnotationProvider e
return urlSet;
}
+
+ @Override
+ public Set<URL> getBaseUrls(ExternalContext context) throws IOException
+ {
+ String jarFilesToScanParam = MyfacesConfig.getCurrentInstance(context).getGaeJsfJarFiles();
+ jarFilesToScanParam = jarFilesToScanParam != null ? jarFilesToScanParam.trim() : null;
+ if (ContainerUtils.isRunningOnGoogleAppEngine(context) &&
+ jarFilesToScanParam != null &&
+ jarFilesToScanParam.length() > 0)
+ {
+ Set<URL> urlSet = new HashSet<URL>();
+
+ //This usually happens when maven-jetty-plugin is used
+ //Scan jars looking for paths including META-INF/faces-config.xml
+ Enumeration<URL> resources = getClassLoader().getResources(FACES_CONFIG_IMPLICIT);
+ while (resources.hasMoreElements())
+ {
+ urlSet.add(resources.nextElement());
+ }
+
+ Collection<URL> urlsGAE = GAEUtils.searchInWebLib(
+ context, getClassLoader(), jarFilesToScanParam, META_INF_PREFIX, FACES_CONFIG_SUFFIX);
+ if (urlsGAE != null)
+ {
+ urlSet.addAll(urlsGAE);
+ }
+ return urlSet;
+ }
+ else
+ {
+ return getBaseUrls();
+ }
+ }
protected Collection<Class<?>> getAnnotatedMetaInfClasses(ExternalContext ctx, Set<URL> urls)
{
@@ -244,6 +298,45 @@ public class DefaultAnnotationProvider e
}
return Collections.emptyList();
}
+
+ protected Collection<Class<?>> getGAEAnnotatedMetaInfClasses(ExternalContext context, String filter)
+ {
+ if (!filter.equals("none"))
+ {
+ String[] jarFilesToScan = StringUtils.trim(StringUtils.splitLongString(filter, ','));
+ Set<String> paths = context.getResourcePaths(WEB_LIB_PREFIX);
+ if (paths != null)
+ {
+ List<Class<?>> list = new ArrayList<Class<?>>();
+ for (Object pathObject : paths)
+ {
+ String path = (String) pathObject;
+ if (path.endsWith(".jar") && GAEUtils.wildcardMatch(path, jarFilesToScan, GAEUtils.WEB_LIB_PREFIX))
+ {
+ // GAE does not use WAR format, so the app is just uncompressed in a directory
+ // What we need here is just take the path of the file, and open the file as a
+ // jar file. Then, if the jar should be scanned, try to find the required file.
+ try
+ {
+ URL jarUrl = new URL("jar:" + context.getResource(path).toExternalForm() + "!/");
+ JarFile jarFile = JarUtils.getJarFile(jarUrl);
+ if (jarFile != null)
+ {
+ archiveClasses(context, jarFile, list);
+ }
+ }
+ catch(IOException e)
+ {
+ log.log(Level.SEVERE,
+ "IOException when reading jar file for annotations using filter: "+filter, e);
+ }
+ }
+ }
+ return list;
+ }
+ }
+ return Collections.emptyList();
+ }
protected Collection<Class<?>> getAnnotatedMyfacesImplClasses(ExternalContext ctx, URL url)
{
Added: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/util/GAEUtils.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/util/GAEUtils.java?rev=1357494&view=auto
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/util/GAEUtils.java (added)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/util/GAEUtils.java Thu Jul 5 08:49:50 2012
@@ -0,0 +1,590 @@
+/*
+ * 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.myfaces.config.util;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Set;
+import java.util.Stack;
+import java.util.TreeSet;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import javax.faces.context.ExternalContext;
+import org.apache.myfaces.shared.util.StringUtils;
+
+/**
+ * Utility methods to use in Google Application Engine (GAE)
+ *
+ * @author Leonardo Uribe
+ */
+public class GAEUtils
+{
+
+ public static final String WEB_LIB_PREFIX = "/WEB-INF/lib/";
+
+
+ /**
+ * Look in all jars located inside /WEB-INF/lib/ folder for files that has
+ * some specified prefix and suffix. It is a simplification that can be done
+ * in GAE, because no JSF libraries are outside /WEB-INF/lib
+ *
+ * @param context
+ * @param classloader
+ * @param prefix
+ * @param suffix
+ * @return
+ * @throws IOException
+ */
+ public static Collection<URL> searchInWebLib(
+ ExternalContext context, ClassLoader classloader, String filter,
+ String prefix, String suffix) throws IOException
+ {
+ if (!filter.equals("none"))
+ {
+ String[] jarFilesToScan = StringUtils.trim(StringUtils.splitLongString(filter, ','));
+ Set<URL> urlSet = null;
+ Set<String> paths = context.getResourcePaths(WEB_LIB_PREFIX);
+ if (paths != null)
+ {
+ for (Object pathObject : paths)
+ {
+ String path = (String) pathObject;
+ if (path.endsWith(".jar") && wildcardMatch(path, jarFilesToScan, WEB_LIB_PREFIX))
+ {
+ // GAE does not use WAR format, so the app is just uncompressed in a directory
+ // What we need here is just take the path of the file, and open the file as a
+ // jar file. Then, if the jar should be scanned, try to find the required file.
+ URL jarUrl = new URL("jar:" + context.getResource(path).toExternalForm() + "!/");
+ JarFile jarFile = JarUtils.getJarFile(jarUrl);
+
+ Enumeration<JarEntry> entries = jarFile.entries();
+ while (entries.hasMoreElements())
+ {
+ JarEntry entry = entries.nextElement();
+ if (entry.isDirectory())
+ {
+ continue; // This is a directory
+ }
+ String name = entry.getName();
+ if (!name.startsWith(prefix))
+ {
+ continue; // Attribute files
+ }
+ if (name.endsWith(suffix))
+ {
+ // Get it from classloader, because no URL can be
+ // derived from JarEntry
+ Enumeration<URL> alternateFacesConfigs = classloader.getResources(name);
+ while (alternateFacesConfigs.hasMoreElements())
+ {
+ if (urlSet == null)
+ {
+ urlSet = new TreeSet<URL>();
+ }
+ urlSet.add(alternateFacesConfigs.nextElement());
+ }
+ }
+ }
+ }
+ }
+ }
+ return urlSet;
+ }
+ return null;
+ }
+
+ public static boolean wildcardMatch(String filename, String[] wildcardMatchers, String prefix)
+ {
+ for (String matcher : wildcardMatchers)
+ {
+ if (wildcardMatch(filename, prefix + matcher))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // NOTE: CODE TAKEN FROM COMMONS-IO AND REFACTORED TO USE INSIDE GAE
+ //-----------------------------------------------------------------------
+ /**
+ * Checks a filename to see if it matches the specified wildcard matcher,
+ * always testing case-sensitive. <p> The wildcard matcher uses the
+ * characters '?' and '*' to represent a single or multiple (zero or more)
+ * wildcard characters. This is the same as often found on Dos/Unix command
+ * lines. The check is case-sensitive always.
+ * <pre>
+ * wildcardMatch("c.txt", "*.txt") --> true
+ * wildcardMatch("c.txt", "*.jpg") --> false
+ * wildcardMatch("a/b/c.txt", "a/b/*") --> true
+ * wildcardMatch("c.txt", "*.???") --> true
+ * wildcardMatch("c.txt", "*.????") --> false
+ * </pre> N.B. the sequence "*?" does not work properly at present in match
+ * strings.
+ *
+ * @param filename the filename to match on
+ * @param wildcardMatcher the wildcard string to match against
+ * @return true if the filename matches the wilcard string
+ * @see IOCase#SENSITIVE
+ */
+ static boolean wildcardMatch(String filename, String wildcardMatcher)
+ {
+ return wildcardMatch(filename, wildcardMatcher, IOCase.SENSITIVE);
+ }
+
+ /**
+ * Checks a filename to see if it matches the specified wildcard matcher
+ * using the case rules of the system. <p> The wildcard matcher uses the
+ * characters '?' and '*' to represent a single or multiple (zero or more)
+ * wildcard characters. This is the same as often found on Dos/Unix command
+ * lines. The check is case-sensitive on Unix and case-insensitive on
+ * Windows.
+ * <pre>
+ * wildcardMatch("c.txt", "*.txt") --> true
+ * wildcardMatch("c.txt", "*.jpg") --> false
+ * wildcardMatch("a/b/c.txt", "a/b/*") --> true
+ * wildcardMatch("c.txt", "*.???") --> true
+ * wildcardMatch("c.txt", "*.????") --> false
+ * </pre> N.B. the sequence "*?" does not work properly at present in match
+ * strings.
+ *
+ * @param filename the filename to match on
+ * @param wildcardMatcher the wildcard string to match against
+ * @return true if the filename matches the wilcard string
+ * @see IOCase#SYSTEM
+ */
+ static boolean wildcardMatchOnSystem(String filename, String wildcardMatcher)
+ {
+ //return wildcardMatch(filename, wildcardMatcher, IOCase.SYSTEM);
+ return wildcardMatch(filename, wildcardMatcher, IOCase.SENSITIVE);
+ }
+
+ /**
+ * Checks a filename to see if it matches the specified wildcard matcher
+ * allowing control over case-sensitivity. <p> The wildcard matcher uses the
+ * characters '?' and '*' to represent a single or multiple (zero or more)
+ * wildcard characters. N.B. the sequence "*?" does not work properly at
+ * present in match strings.
+ *
+ * @param filename the filename to match on
+ * @param wildcardMatcher the wildcard string to match against
+ * @param caseSensitivity what case sensitivity rule to use, null means
+ * case-sensitive
+ * @return true if the filename matches the wilcard string
+ * @since 1.3
+ */
+ static boolean wildcardMatch(String filename, String wildcardMatcher, IOCase caseSensitivity)
+ {
+ if (filename == null && wildcardMatcher == null)
+ {
+ return true;
+ }
+ if (filename == null || wildcardMatcher == null)
+ {
+ return false;
+ }
+ if (caseSensitivity == null)
+ {
+ caseSensitivity = IOCase.SENSITIVE;
+ }
+ String[] wcs = splitOnTokens(wildcardMatcher);
+ boolean anyChars = false;
+ int textIdx = 0;
+ int wcsIdx = 0;
+ Stack<int[]> backtrack = new Stack<int[]>();
+
+ // loop around a backtrack stack, to handle complex * matching
+ do
+ {
+ if (backtrack.size() > 0)
+ {
+ int[] array = backtrack.pop();
+ wcsIdx = array[0];
+ textIdx = array[1];
+ anyChars = true;
+ }
+
+ // loop whilst tokens and text left to process
+ while (wcsIdx < wcs.length)
+ {
+
+ if (wcs[wcsIdx].equals("?"))
+ {
+ // ? so move to next text char
+ textIdx++;
+ if (textIdx > filename.length())
+ {
+ break;
+ }
+ anyChars = false;
+
+ }
+ else if (wcs[wcsIdx].equals("*"))
+ {
+ // set any chars status
+ anyChars = true;
+ if (wcsIdx == wcs.length - 1)
+ {
+ textIdx = filename.length();
+ }
+
+ }
+ else
+ {
+ // matching text token
+ if (anyChars)
+ {
+ // any chars then try to locate text token
+ textIdx = caseSensitivity.checkIndexOf(filename, textIdx, wcs[wcsIdx]);
+ if (textIdx == -1)
+ {
+ // token not found
+ break;
+ }
+ int repeat = caseSensitivity.checkIndexOf(filename, textIdx + 1, wcs[wcsIdx]);
+ if (repeat >= 0)
+ {
+ backtrack.push(new int[]
+ {
+ wcsIdx, repeat
+ });
+ }
+ }
+ else
+ {
+ // matching from current position
+ if (!caseSensitivity.checkRegionMatches(filename, textIdx, wcs[wcsIdx]))
+ {
+ // couldnt match token
+ break;
+ }
+ }
+
+ // matched text token, move text index to end of matched token
+ textIdx += wcs[wcsIdx].length();
+ anyChars = false;
+ }
+
+ wcsIdx++;
+ }
+
+ // full match
+ if (wcsIdx == wcs.length && textIdx == filename.length())
+ {
+ return true;
+ }
+
+ } while (backtrack.size() > 0);
+
+ return false;
+ }
+
+ /**
+ * Splits a string into a number of tokens. The text is split by '?' and
+ * '*'. Where multiple '*' occur consecutively they are collapsed into a
+ * single '*'.
+ *
+ * @param text the text to split
+ * @return the array of tokens, never null
+ */
+ static String[] splitOnTokens(String text)
+ {
+ // used by wildcardMatch
+ // package level so a unit test may run on this
+
+ if (text.indexOf('?') == -1 && text.indexOf('*') == -1)
+ {
+ return new String[]
+ {
+ text
+ };
+ }
+
+ char[] array = text.toCharArray();
+ ArrayList<String> list = new ArrayList<String>();
+ StringBuilder buffer = new StringBuilder();
+ for (int i = 0; i < array.length; i++)
+ {
+ if (array[i] == '?' || array[i] == '*')
+ {
+ if (buffer.length() != 0)
+ {
+ list.add(buffer.toString());
+ buffer.setLength(0);
+ }
+ if (array[i] == '?')
+ {
+ list.add("?");
+ }
+ else if (list.isEmpty()
+ || i > 0 && list.get(list.size() - 1).equals("*") == false)
+ {
+ list.add("*");
+ }
+ }
+ else
+ {
+ buffer.append(array[i]);
+ }
+ }
+ if (buffer.length() != 0)
+ {
+ list.add(buffer.toString());
+ }
+
+ return list.toArray(new String[list.size()]);
+ }
+
+ final static class IOCase implements Serializable
+ {
+
+ /**
+ * The constant for case sensitive regardless of operating system.
+ */
+ public static final IOCase SENSITIVE = new IOCase("Sensitive", true);
+ /**
+ * The constant for case insensitive regardless of operating system.
+ */
+ public static final IOCase INSENSITIVE = new IOCase("Insensitive", false);
+ /**
+ * The constant for case sensitivity determined by the current operating
+ * system. Windows is case-insensitive when comparing filenames, Unix is
+ * case-sensitive. <p> <strong>Note:</strong> This only caters for
+ * Windows and Unix. Other operating systems (e.g. OSX and OpenVMS) are
+ * treated as case sensitive if they use the Unix file separator and
+ * case-insensitive if they use the Windows file separator (see {@link java.io.File#separatorChar}).
+ * <p> If you derialize this constant of Windows, and deserialize on
+ * Unix, or vice versa, then the value of the case-sensitivity flag will
+ * change.
+ */
+ //public static final IOCase SYSTEM = new IOCase("System", !FilenameUtils.isSystemWindows());
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = -6343169151696340687L;
+ /**
+ * The enumeration name.
+ */
+ private final String name;
+ /**
+ * The sensitivity flag.
+ */
+ private final transient boolean sensitive;
+
+ //-----------------------------------------------------------------------
+ /**
+ * Factory method to create an IOCase from a name.
+ *
+ * @param name the name to find
+ * @return the IOCase object
+ * @throws IllegalArgumentException if the name is invalid
+ */
+ public static IOCase forName(String name)
+ {
+ if (IOCase.SENSITIVE.name.equals(name))
+ {
+ return IOCase.SENSITIVE;
+ }
+ if (IOCase.INSENSITIVE.name.equals(name))
+ {
+ return IOCase.INSENSITIVE;
+ }
+ //if (IOCase.SYSTEM.name.equals(name)){
+ // return IOCase.SYSTEM;
+ //}
+ throw new IllegalArgumentException("Invalid IOCase name: " + name);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Private constructor.
+ *
+ * @param name the name
+ * @param sensitive the sensitivity
+ */
+ private IOCase(String name, boolean sensitive)
+ {
+ this.name = name;
+ this.sensitive = sensitive;
+ }
+
+ /**
+ * Replaces the enumeration from the stream with a real one. This
+ * ensures that the correct flag is set for SYSTEM.
+ *
+ * @return the resolved object
+ */
+ private Object readResolve()
+ {
+ return forName(name);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the name of the constant.
+ *
+ * @return the name of the constant
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Does the object represent case sensitive comparison.
+ *
+ * @return true if case sensitive
+ */
+ public boolean isCaseSensitive()
+ {
+ return sensitive;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Compares two strings using the case-sensitivity rule. <p> This method
+ * mimics {@link String#compareTo} but takes case-sensitivity into
+ * account.
+ *
+ * @param str1 the first string to compare, not null
+ * @param str2 the second string to compare, not null
+ * @return true if equal using the case rules
+ * @throws NullPointerException if either string is null
+ */
+ public int checkCompareTo(String str1, String str2)
+ {
+ if (str1 == null || str2 == null)
+ {
+ throw new NullPointerException("The strings must not be null");
+ }
+ return sensitive ? str1.compareTo(str2) : str1.compareToIgnoreCase(str2);
+ }
+
+ /**
+ * Compares two strings using the case-sensitivity rule. <p> This method
+ * mimics {@link String#equals} but takes case-sensitivity into account.
+ *
+ * @param str1 the first string to compare, not null
+ * @param str2 the second string to compare, not null
+ * @return true if equal using the case rules
+ * @throws NullPointerException if either string is null
+ */
+ public boolean checkEquals(String str1, String str2)
+ {
+ if (str1 == null || str2 == null)
+ {
+ throw new NullPointerException("The strings must not be null");
+ }
+ return sensitive ? str1.equals(str2) : str1.equalsIgnoreCase(str2);
+ }
+
+ /**
+ * Checks if one string starts with another using the case-sensitivity
+ * rule. <p> This method mimics {@link String#startsWith(String)} but
+ * takes case-sensitivity into account.
+ *
+ * @param str the string to check, not null
+ * @param start the start to compare against, not null
+ * @return true if equal using the case rules
+ * @throws NullPointerException if either string is null
+ */
+ public boolean checkStartsWith(String str, String start)
+ {
+ return str.regionMatches(!sensitive, 0, start, 0, start.length());
+ }
+
+ /**
+ * Checks if one string ends with another using the case-sensitivity
+ * rule. <p> This method mimics {@link String#endsWith} but takes
+ * case-sensitivity into account.
+ *
+ * @param str the string to check, not null
+ * @param end the end to compare against, not null
+ * @return true if equal using the case rules
+ * @throws NullPointerException if either string is null
+ */
+ public boolean checkEndsWith(String str, String end)
+ {
+ int endLen = end.length();
+ return str.regionMatches(!sensitive, str.length() - endLen, end, 0, endLen);
+ }
+
+ /**
+ * Checks if one string contains another starting at a specific index
+ * using the case-sensitivity rule. <p> This method mimics parts of {@link String#indexOf(String, int)}
+ * but takes case-sensitivity into account.
+ *
+ * @param str the string to check, not null
+ * @param strStartIndex the index to start at in str
+ * @param search the start to search for, not null
+ * @return the first index of the search String, -1 if no match or {@code null}
+ * string input
+ * @throws NullPointerException if either string is null
+ * @since 2.0
+ */
+ public int checkIndexOf(String str, int strStartIndex, String search)
+ {
+ int endIndex = str.length() - search.length();
+ if (endIndex >= strStartIndex)
+ {
+ for (int i = strStartIndex; i <= endIndex; i++)
+ {
+ if (checkRegionMatches(str, i, search))
+ {
+ return i;
+ }
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Checks if one string contains another at a specific index using the
+ * case-sensitivity rule. <p> This method mimics parts of {@link
+ * String#regionMatches(boolean, int, String, int, int)}
+ * but takes case-sensitivity into account.
+ *
+ * @param str the string to check, not null
+ * @param strStartIndex the index to start at in str
+ * @param search the start to search for, not null
+ * @return true if equal using the case rules
+ * @throws NullPointerException if either string is null
+ */
+ public boolean checkRegionMatches(String str, int strStartIndex, String search)
+ {
+ return str.regionMatches(!sensitive, strStartIndex, search, 0, search.length());
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets a string describing the sensitivity.
+ *
+ * @return a string describing the sensitivity
+ */
+ @Override
+ public String toString()
+ {
+ return name;
+ }
+ }
+}
Added: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/util/JarUtils.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/util/JarUtils.java?rev=1357494&view=auto
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/util/JarUtils.java (added)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/util/JarUtils.java Thu Jul 5 08:49:50 2012
@@ -0,0 +1,87 @@
+/*
+ * 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.myfaces.config.util;
+
+import java.io.IOException;
+import java.net.JarURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.jar.JarFile;
+
+/**
+ *
+ * @author Leonardo Uribe
+ */
+public class JarUtils
+{
+
+ public static JarFile getJarFile(URL url) throws IOException
+ {
+ URLConnection conn = url.openConnection();
+ conn.setUseCaches(false);
+ conn.setDefaultUseCaches(false);
+
+ JarFile jarFile;
+ if (conn instanceof JarURLConnection)
+ {
+ jarFile = ((JarURLConnection) conn).getJarFile();
+ }
+ else
+ {
+ jarFile = _getAlternativeJarFile(url);
+ }
+ return jarFile;
+ }
+
+ /**
+ * taken from org.apache.myfaces.view.facelets.util.Classpath
+ *
+ * For URLs to JARs that do not use JarURLConnection - allowed by the servlet spec - attempt to produce a JarFile
+ * object all the same. Known servlet engines that function like this include Weblogic and OC4J. This is not a full
+ * solution, since an unpacked WAR or EAR will not have JAR "files" as such.
+ */
+ private static JarFile _getAlternativeJarFile(URL url) throws IOException
+ {
+ String urlFile = url.getFile();
+
+ // Trim off any suffix - which is prefixed by "!/" on Weblogic
+ int separatorIndex = urlFile.indexOf("!/");
+
+ // OK, didn't find that. Try the less safe "!", used on OC4J
+ if (separatorIndex == -1)
+ {
+ separatorIndex = urlFile.indexOf('!');
+ }
+
+ if (separatorIndex != -1)
+ {
+ String jarFileUrl = urlFile.substring(0, separatorIndex);
+ // And trim off any "file:" prefix.
+ if (jarFileUrl.startsWith("file:"))
+ {
+ jarFileUrl = jarFileUrl.substring("file:".length());
+ }
+
+ return new JarFile(jarFileUrl);
+ }
+
+ return null;
+ }
+
+}
Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/spi/AnnotationProvider.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/spi/AnnotationProvider.java?rev=1357494&r1=1357493&r2=1357494&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/spi/AnnotationProvider.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/spi/AnnotationProvider.java Thu Jul 5 08:49:50 2012
@@ -92,10 +92,26 @@ public abstract class AnnotationProvider
* the "META-INF" directory (considered <code>applicationConfigurationResources)<code></li>
* </ol>
*
+ * @deprecated
* @return
*/
+ @Deprecated
public abstract Set<URL> getBaseUrls() throws IOException;
+ /**
+ * Same as getBaseUrls(), but with the ExternalContext reference.
+ * By default it calls to getBaseUrls()
+ *
+ * @since 2.1.9, 2.0.15
+ * @param ctx
+ * @return
+ * @throws IOException
+ */
+ public Set<URL> getBaseUrls(ExternalContext ctx) throws IOException
+ {
+ return getBaseUrls();
+ }
+
public AnnotationProvider getWrapped()
{
return null;
Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/spi/AnnotationProviderWrapper.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/spi/AnnotationProviderWrapper.java?rev=1357494&r1=1357493&r2=1357494&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/spi/AnnotationProviderWrapper.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/spi/AnnotationProviderWrapper.java Thu Jul 5 08:49:50 2012
@@ -52,4 +52,8 @@ public abstract class AnnotationProvider
return getWrapped().getBaseUrls();
}
+ public Set<URL> getBaseUrls(ExternalContext ctx) throws IOException
+ {
+ return getWrapped().getBaseUrls(ctx);
+ }
}
Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/compiler/DefaultFaceletConfigResourceProvider.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/compiler/DefaultFaceletConfigResourceProvider.java?rev=1357494&r1=1357493&r2=1357494&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/compiler/DefaultFaceletConfigResourceProvider.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/compiler/DefaultFaceletConfigResourceProvider.java Thu Jul 5 08:49:50 2012
@@ -25,9 +25,12 @@ import java.util.Collection;
import java.util.List;
import javax.faces.context.ExternalContext;
+import org.apache.myfaces.shared.config.MyfacesConfig;
import org.apache.myfaces.shared.util.ClassUtils;
import org.apache.myfaces.spi.FaceletConfigResourceProvider;
+import org.apache.myfaces.util.ContainerUtils;
+import org.apache.myfaces.config.util.GAEUtils;
import org.apache.myfaces.view.facelets.util.Classpath;
/**
@@ -52,13 +55,28 @@ public class DefaultFaceletConfigResourc
{
List<URL> urlSet = new ArrayList<URL>();
- //Scan files inside META-INF ending with .faces-config.xml
- URL[] urls = Classpath.search(getClassLoader(), META_INF_PREFIX, FACELET_TAGLIB_SUFFIX);
- for (int i = 0; i < urls.length; i++)
+ String jarFilesToScanParam = MyfacesConfig.getCurrentInstance(context).getGaeJsfJarFiles();
+ jarFilesToScanParam = jarFilesToScanParam != null ? jarFilesToScanParam.trim() : null;
+ if (ContainerUtils.isRunningOnGoogleAppEngine(context) &&
+ jarFilesToScanParam != null &&
+ jarFilesToScanParam.length() > 0)
{
- urlSet.add(urls[i]);
+ Collection<URL> urlsGAE = GAEUtils.searchInWebLib(
+ context, getClassLoader(), jarFilesToScanParam, META_INF_PREFIX, FACELET_TAGLIB_SUFFIX);
+ if (urlsGAE != null)
+ {
+ urlSet.addAll(urlsGAE);
+ }
+ }
+ else
+ {
+ //Scan files inside META-INF ending with .faces-config.xml
+ URL[] urls = Classpath.search(getClassLoader(), META_INF_PREFIX, FACELET_TAGLIB_SUFFIX);
+ for (int i = 0; i < urls.length; i++)
+ {
+ urlSet.add(urls[i]);
+ }
}
-
return urlSet;
}
Modified: myfaces/core/trunk/shared/src/main/java/org/apache/myfaces/shared/config/MyfacesConfig.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/shared/src/main/java/org/apache/myfaces/shared/config/MyfacesConfig.java?rev=1357494&r1=1357493&r2=1357494&view=diff
==============================================================================
--- myfaces/core/trunk/shared/src/main/java/org/apache/myfaces/shared/config/MyfacesConfig.java (original)
+++ myfaces/core/trunk/shared/src/main/java/org/apache/myfaces/shared/config/MyfacesConfig.java Thu Jul 5 08:49:50 2012
@@ -390,6 +390,34 @@ public class MyfacesConfig
public final static String INIT_PARAM_SUPPORT_JSP_AND_FACES_EL = "org.apache.myfaces.SUPPORT_JSP_AND_FACES_EL";
public final static boolean INIT_PARAM_SUPPORT_JSP_AND_FACES_EL_DEFAULT = true;
+ /**
+ * When the application runs inside Google Application Engine container (GAE),
+ * indicate which jar files should be scanned for files (faces-config, facelets taglib
+ * or annotations). It accept simple wildcard patterns like myfavoritejsflib-*.jar or
+ * myfavoritejsflib-1.1.?.jar. By default, all the classpath is scanned for files
+ * annotations (so it adds an small delay on startup).
+ */
+ @JSFWebConfigParam(since = "2.1.8, 2.0.14", expectedValues="none, myfavoritejsflib-*.jar",
+ tags="performance, GAE")
+ public static final String INIT_PARAM_GAE_JSF_JAR_FILES = "org.apache.myfaces.GAE_JSF_JAR_FILES";
+ public final static String INIT_PARAM_GAE_JSF_JAR_FILES_DEFAULT = null;
+
+ /**
+ * When the application runs inside Google Application Engine container (GAE),
+ * indicate which jar files should be scanned for annotations. This param overrides
+ * org.apache.myfaces.GAE_JSF_JAR_FILES behavior that tries to find faces-config.xml or
+ * files ending with .faces-config.xml in /META-INF folder and if that so, try to
+ * find JSF annotations in the whole jar file. It accept simple wildcard patterns
+ * like myfavoritejsflib-*.jar or myfavoritejsflib-1.1.?.jar.
+ * By default, all the classpath is scanned for annotations (so it adds an small
+ * delay on startup).
+ */
+ @JSFWebConfigParam(since = "2.1.8, 2.0.14", expectedValues="none, myfavoritejsflib-*.jar",
+ tags="performance, GAE")
+ public static final String INIT_PARAM_GAE_JSF_ANNOTATIONS_JAR_FILES =
+ "org.apache.myfaces.GAE_JSF_ANNOTATIONS_JAR_FILES";
+ public final static String INIT_PARAM_GAE_JSF_ANNOTATIONS_JAR_FILES_DEFAULT = null;
+
private boolean _prettyHtml;
private boolean _detectJavascript;
private boolean _allowJavascript;
@@ -419,6 +447,8 @@ public class MyfacesConfig
private boolean _viewUniqueIdsCacheEnabled;
private int _componentUniqueIdsCacheSize;
private boolean _supportJSPAndFacesEL;
+ private String _gaeJsfJarFiles;
+ private String _gaeJsfAnnotationsJarFiles;
private static final boolean TOMAHAWK_AVAILABLE;
private static final boolean MYFACES_IMPL_AVAILABLE;
@@ -517,6 +547,8 @@ public class MyfacesConfig
setViewUniqueIdsCacheEnabled(INIT_PARAM_VIEW_UNIQUE_IDS_CACHE_ENABLED_DEFAULT);
setComponentUniqueIdsCacheSize(INIT_PARAM_COMPONENT_UNIQUE_IDS_CACHE_SIZE_DEFAULT);
setSupportJSPAndFacesEL(INIT_PARAM_SUPPORT_JSP_AND_FACES_EL_DEFAULT);
+ setGaeJsfJarFiles(INIT_PARAM_GAE_JSF_JAR_FILES_DEFAULT);
+ setGaeJsfAnnotationsJarFiles(INIT_PARAM_GAE_JSF_ANNOTATIONS_JAR_FILES_DEFAULT);
}
private static MyfacesConfig createAndInitializeMyFacesConfig(ExternalContext extCtx)
@@ -619,6 +651,11 @@ public class MyfacesConfig
myfacesConfig.setSupportJSPAndFacesEL(WebConfigParamUtils.getBooleanInitParameter(extCtx,
INIT_PARAM_SUPPORT_JSP_AND_FACES_EL, INIT_PARAM_SUPPORT_JSP_AND_FACES_EL_DEFAULT));
+ myfacesConfig.setGaeJsfJarFiles(WebConfigParamUtils.getStringInitParameter(extCtx,
+ INIT_PARAM_GAE_JSF_JAR_FILES, INIT_PARAM_GAE_JSF_JAR_FILES_DEFAULT));
+ myfacesConfig.setGaeJsfAnnotationsJarFiles(WebConfigParamUtils.getStringInitParameter(extCtx,
+ INIT_PARAM_GAE_JSF_ANNOTATIONS_JAR_FILES, INIT_PARAM_GAE_JSF_ANNOTATIONS_JAR_FILES_DEFAULT));
+
if (TOMAHAWK_AVAILABLE)
{
myfacesConfig.setDetectJavascript(getBooleanInitParameter(extCtx, INIT_PARAM_DETECT_JAVASCRIPT,
@@ -1116,4 +1153,25 @@ public class MyfacesConfig
{
this._componentUniqueIdsCacheSize = componentUniqueIdsCacheSize;
}
+
+ public String getGaeJsfJarFiles()
+ {
+ return _gaeJsfJarFiles;
+ }
+
+ public void setGaeJsfJarFiles(String gaeJsfJarFiles)
+ {
+ this._gaeJsfJarFiles = gaeJsfJarFiles;
+ }
+
+ public String getGaeJsfAnnotationsJarFiles()
+ {
+ return _gaeJsfAnnotationsJarFiles;
+ }
+
+ public void setGaeJsfAnnotationsJarFiles(String gaeJsfAnnotationsJarFiles)
+ {
+ this._gaeJsfAnnotationsJarFiles = gaeJsfAnnotationsJarFiles;
+ }
+
}