/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
// Util.java
package com.sun.faces.util;
import static com.sun.faces.util.MessageUtils.ILLEGAL_ATTEMPT_SETTING_APPLICATION_ARTIFACT_ID;
import static com.sun.faces.util.MessageUtils.NAMED_OBJECT_NOT_FOUND_ERROR_MESSAGE_ID;
import static com.sun.faces.util.MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID;
import static com.sun.faces.util.MessageUtils.NULL_VIEW_ID_ERROR_MESSAGE_ID;
import static com.sun.faces.util.MessageUtils.getExceptionMessageString;
import static com.sun.faces.util.RequestStateManager.INVOCATION_PATH;
import static java.util.Collections.emptyList;
import static java.util.logging.Level.FINE;
import static java.util.logging.Level.SEVERE;
import java.beans.FeatureDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import javax.el.ELResolver;
import javax.el.ValueExpression;
import javax.enterprise.inject.spi.BeanManager;
import javax.faces.FacesException;
import javax.faces.application.Application;
import javax.faces.application.ProjectStage;
import javax.faces.application.StateManager;
import javax.faces.application.ViewHandler;
import javax.faces.component.NamingContainer;
import javax.faces.component.UIComponent;
import javax.faces.component.UINamingContainer;
import javax.faces.component.UIViewRoot;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.event.AbortProcessingException;
import javax.faces.render.ResponseStateManager;
import javax.faces.webapp.FacesServlet;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;
import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.TransformerFactory;
import javax.xml.validation.SchemaFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.xml.sax.InputSource;
import com.sun.faces.RIConstants;
import com.sun.faces.application.ApplicationAssociate;
import com.sun.faces.config.WebConfiguration;
import com.sun.faces.io.FastStringWriter;
/**
* <B>Util</B> is a class ...
* <p/>
* <B>Lifetime And Scope</B> <P>
*
*/
public class Util {
// Log instance for this class
private static final Logger LOGGER = FacesLogger.APPLICATION.getLogger();
// README - make sure to add the message identifier constant
// (ex: Util.CONVERSION_ERROR_MESSAGE_ID) and the number of substitution
// parameters to test/com/sun/faces/util/TestUtil_messages (see comment there).
/**
* Flag that, when true, enables special behavior in the RI to enable
* unit testing.
*/
private static boolean unitTestModeEnabled = false;
/**
* RegEx patterns
*/
private static final String PATTERN_CACKE_KEY = RIConstants.FACES_PREFIX + "patternCache";
private static final String FACES_SERVLET_CLASS = FacesServlet.class.getName();
private Util() {
throw new IllegalStateException();
}
private static Map<String, Pattern> getPatternCache(Map<String, Object> appMap) {
@SuppressWarnings("unchecked")
Map<String, Pattern> result = (Map<String, Pattern>) appMap.get(PATTERN_CACKE_KEY);
if (result == null) {
result = new LRUMap<>(15);
appMap.put(PATTERN_CACKE_KEY, result);
}
return result;
}
private static Map<String, Pattern> getPatternCache(ServletContext sc) {
@SuppressWarnings("unchecked")
Map<String, Pattern> result = (Map<String, Pattern>) sc.getAttribute(PATTERN_CACKE_KEY);
if (result == null) {
result = new LRUMap<>(15);
sc.setAttribute(PATTERN_CACKE_KEY, result);
}
return result;
}
private static Collection<String> getFacesServletMappings(ServletContext servletContext) {
ServletRegistration facesRegistration = getExistingFacesServletRegistration(servletContext);
if (facesRegistration != null) {
return facesRegistration.getMappings();
}
return emptyList();
}
private static ServletRegistration getExistingFacesServletRegistration(ServletContext servletContext) {
Map<String,? extends ServletRegistration> existing = servletContext.getServletRegistrations();
for (ServletRegistration registration : existing.values()) {
if (FACES_SERVLET_CLASS.equals(registration.getClassName())) {
return registration;
}
}
return null;
}
/**
* <p>
* Convenience method for determining if the request associated
* with the specified <code>FacesContext</code> is a PortletRequest
* submitted by the JSR-301 bridge.
* </p>
* @param context the <code>FacesContext</code> associated with
* the request.
*/
public static boolean isPortletRequest (FacesContext context) {
return context.getExternalContext().getRequestMap().get("javax.portlet.faces.phase") != null;
}
/**
* <p>Factory method for creating the varius JSF listener
* instances that may be referenced by <code>type</code>
* or <code>binding</code>.</p>
* <p>If <code>binding</code> is not <code>null</code>
* and the evaluation result is not <code>null</code> return
* that instance. Otherwise try to instantiate an instances
* based on <code>type</code>.</p>
*
* @param type the <code>Listener</code> type
* @param binding a <code>ValueExpression</code> which resolves
* to a <code>Listener</code> instance
* @return a <code>Listener</code> instance based off the provided
* <code>type</code> and <binding>
*/
public static Object getListenerInstance(ValueExpression type,
ValueExpression binding) {
FacesContext faces = FacesContext.getCurrentInstance();
Object instance = null;
if (faces == null) {
return null;
}
if (binding != null) {
instance = binding.getValue(faces.getELContext());
}
if (instance == null && type != null) {
try {
instance = ReflectionUtils.newInstance(((String) type.getValue(faces.getELContext())));
} catch (InstantiationException | IllegalAccessException e) {
throw new AbortProcessingException(e.getMessage(), e);
}
if (binding != null) {
binding.setValue(faces.getELContext(), instance);
}
}
return instance;
}
public static void setUnitTestModeEnabled(boolean enabled) {
unitTestModeEnabled = enabled;
}
public static boolean isUnitTestModeEnabled() {
return unitTestModeEnabled;
}
public static TransformerFactory createTransformerFactory() {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
TransformerFactory factory;
try {
Thread.currentThread().setContextClassLoader(Util.class.getClassLoader());
factory = TransformerFactory.newInstance();
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
return factory;
}
public static SAXParserFactory createSAXParserFactory() {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
SAXParserFactory factory;
try {
Thread.currentThread().setContextClassLoader(Util.class.getClassLoader());
factory = SAXParserFactory.newInstance();
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
return factory;
}
public static DocumentBuilderFactory createDocumentBuilderFactory() {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
DocumentBuilderFactory factory;
try {
Thread.currentThread().setContextClassLoader(Util.class.getClassLoader());
factory = DocumentBuilderFactory.newInstance();
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
return factory;
}
public static SchemaFactory createSchemaFactory(String uri) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
SchemaFactory factory;
try {
Thread.currentThread().setContextClassLoader(Util.class.getClassLoader());
factory = SchemaFactory.newInstance(uri);
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
return factory;
}
public static Class loadClass(String name, Object fallbackClass) throws ClassNotFoundException {
ClassLoader loader = Util.getCurrentLoader(fallbackClass);
String[] primitiveNames = { "byte", "short", "int", "long", "float", "double", "boolean", "char" };
Class<?>[] primitiveClasses = { byte.class, short.class, int.class, long.class, float.class, double.class, boolean.class, char.class };
for (int i = 0; i < primitiveNames.length; i++) {
if (primitiveNames[i].equals(name)) {
return primitiveClasses[i];
}
}
return Class.forName(name, true, loader);
}
public static Class<?> loadClass2(String name, Object fallbackClass) {
try {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
if (loader == null) {
loader = fallbackClass.getClass().getClassLoader();
}
return Class.forName(name, true, loader);
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
@SuppressWarnings("unchecked")
public static <T> T newInstance(Class<?> clazz) {
try {
return (T) clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
public static ClassLoader getCurrentLoader(Object fallbackClass) {
ClassLoader loader = getContextClassLoader();
if (loader == null) {
loader = fallbackClass.getClass().getClassLoader();
}
return loader;
}
private static ClassLoader getContextClassLoader() {
if (System.getSecurityManager() == null) {
return Thread.currentThread().getContextClassLoader();
} else {
return (ClassLoader)
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
@Override
public java.lang.Object run() {
return Thread.currentThread().getContextClassLoader();
}
});
}
}
/**
* <p>
* Identify and return the class loader that is associated with the calling web application.
* </p>
*
* @throws FacesException if the web application class loader cannot be identified
*/
public static ClassLoader getContextClassLoader2() throws FacesException {
// J2EE 1.3 (and later) containers are required to make the
// web application class loader visible through the context
// class loader of the current thread.
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null) {
throw new FacesException("getContextClassLoader");
}
return classLoader;
}
public static String removeAllButLastSlashPathSegment(String input) {
// Trim the leading lastSlash, if any.
if (input.charAt(0) == '/') {
input = input.substring(1);
}
int len = input.length();
// Trim the trailing lastSlash, if any.
if (input.charAt(len - 1) == '/') {
input = input.substring(0, len - 1);
}
// Trim any path segments that remain, leaving only the
// last path segment.
int slash = input.lastIndexOf("/");
// Do we have a "/"?
if (-1 != slash) {
input = input.substring(slash + 1);
}
return input;
}
public static String removeAllButNextToLastSlashPathSegment(String input) {
// Trim the leading lastSlash, if any.
if (input.charAt(0) == '/') {
input = input.substring(1);
}
int len = input.length();
// Trim the trailing lastSlash, if any.
if (input.charAt(len - 1) == '/') {
input = input.substring(0, len - 1);
}
// Trim any path segments that remain, leaving only the
// last path segment.
int lastSlash = input.lastIndexOf("/");
// Do we have a "/"?
if (-1 != lastSlash) {
int startOrPreviousSlash = input.lastIndexOf("/", lastSlash - 1);
startOrPreviousSlash = (-1 == startOrPreviousSlash) ? 0 : startOrPreviousSlash;
input = input.substring(startOrPreviousSlash, lastSlash);
}
return input;
}
public static String removeLastPathSegment(String input) {
int slash = input.lastIndexOf("/");
// Do we have a "/"?
if (-1 != slash) {
input = input.substring(0, slash);
}
return input;
}
public static void notNegative(String varname, long number) {
if (number < 0) {
throw new IllegalArgumentException("\"" + varname + "\" is negative");
}
}
public static void notNull(String varname, Object var) {
if (var == null) {
throw new NullPointerException(
getExceptionMessageString(
NULL_PARAMETERS_ERROR_MESSAGE_ID, varname));
}
}
public static void notNullViewId(String viewId) {
if (viewId == null) {
throw new IllegalArgumentException(
getExceptionMessageString(
NULL_VIEW_ID_ERROR_MESSAGE_ID));
}
}
public static void notNullNamedObject(Object object, String objectId, String logMsg) {
if (object == null) {
Object[] params = { objectId };
if (LOGGER.isLoggable(SEVERE)) {
LOGGER.log(SEVERE, logMsg, params);
}
throw new FacesException(
getExceptionMessageString(
NAMED_OBJECT_NOT_FOUND_ERROR_MESSAGE_ID, params));
}
}
public static void canSetAppArtifact(ApplicationAssociate applicationAssociate, String artifactName) {
if (applicationAssociate.hasRequestBeenServiced()) {
throw new IllegalStateException(
getExceptionMessageString(
ILLEGAL_ATTEMPT_SETTING_APPLICATION_ARTIFACT_ID, artifactName));
}
}
public static void notNullAttribute(String attributeName, Object attribute) {
if (attribute == null) {
throw new FacesException("The \"" + attributeName + "\" attribute is required");
}
}
public static ValueExpression getValueExpressionNullSafe(UIComponent component, String name) {
ValueExpression valueExpression = component.getValueExpression(name);
notNullAttribute(name, valueExpression);
return valueExpression;
}
/**
* Returns true if the given string is null or is empty.
*
* @param string The string to be checked on emptiness.
* @return True if the given string is null or is empty.
*/
public static boolean isEmpty(String string) {
return string == null || string.isEmpty();
}
/**
* Returns <code>true</code> if the given array is null or is empty.
*
* @param array The array to be checked on emptiness.
* @return <code>true</code> if the given array is null or is empty.
*/
public static boolean isEmpty(Object[] array) {
return array == null || array.length == 0;
}
/**
* Returns <code>true</code> if the given collection is null or is empty.
*
* @param collection The collection to be checked on emptiness.
* @return <code>true</code> if the given collection is null or is empty.
*/
public static boolean isEmpty(Collection<?> collection) {
return collection == null || collection.isEmpty();
}
/**
* Returns <code>true</code> if the given value is null or is empty. Types of String, Collection, Map, Optional and Array are
* recognized. If none is recognized, then examine the emptiness of the toString() representation instead.
*
* @param value The value to be checked on emptiness.
* @return <code>true</code> if the given value is null or is empty.
*/
public static boolean isEmpty(Object value) {
if (value == null) {
return true;
}
else if (value instanceof String) {
return ((String) value).isEmpty();
}
else if (value instanceof Collection<?>) {
return ((Collection<?>) value).isEmpty();
}
else if (value instanceof Map<?, ?>) {
return ((Map<?, ?>) value).isEmpty();
}
else if (value instanceof Optional<?>) {
return !((Optional<?>)value).isPresent();
}
else if (value.getClass().isArray()) {
return Array.getLength(value) == 0;
}
else {
return value.toString() == null || value.toString().isEmpty();
}
}
/**
* Returns true if all values are empty, false if at least one value is not empty.
* @param values the values to be checked on emptiness
* @return True if all values are empty, false otherwise
*/
public static boolean isAllEmpty(Object... values) {
for (Object value : values) {
if (!isEmpty(value)) {
return false;
}
}
return true;
}
/**
* Returns <code>true</code> if at least one value is empty.
*
* @param values the values to be checked on emptiness
* @return <code>true</code> if any value is empty and <code>false</code> if no values are empty
*/
public static boolean isAnyEmpty(Object... values) {
for (Object value : values) {
if (isEmpty(value)) {
return true;
}
}
return false;
}
public static boolean isAllNull(Object... values) {
for (Object value : values) {
if (value != null) {
return false;
}
}
return true;
}
public static boolean isAnyNull(Object... values) {
for (Object value : values) {
if (value == null) {
return true;
}
}
return false;
}
/**
* Returns <code>true</code> if the given object equals one of the given objects.
* @param <T> The generic object type.
* @param object The object to be checked if it equals one of the given objects.
* @param objects The argument list of objects to be tested for equality.
* @return <code>true</code> if the given object equals one of the given objects.
*/
@SafeVarargs
public static <T> boolean isOneOf(T object, T... objects) {
for (Object other : objects) {
if (object == null ? other == null : object.equals(other)) {
return true;
}
}
return false;
}
/**
* Returns the first non-<code>null</code> object of the argument list, or <code>null</code> if there is no such element.
*
* @param <T> The generic object type.
* @param objects The argument list of objects to be tested for non-<code>null</code>.
* @return The first non-<code>null</code> object of the argument list, or <code>null</code> if there is no such element.
*/
@SafeVarargs
public static <T> T coalesce(T... objects) {
for (T object : objects) {
if (object != null) {
return object;
}
}
return null;
}
public static <T> List<T> reverse(List<T> list) {
int length = list.size();
List<T> result = new ArrayList<>(length);
for (int i = length - 1; i >= 0; i--) {
result.add(list.get(i));
}
return result;
}
/**
* Returns <code>true</code> if the given string starts with one of the given prefixes.
*
* @param string The object to be checked if it starts with one of the given prefixes.
* @param prefixes The argument list of prefixes to be checked
*
* @return <code>true</code> if the given string starts with one of the given prefixes.
*/
public static boolean startsWithOneOf(String string, String... prefixes) {
if (prefixes == null) {
return false;
}
for (String prefix : prefixes) {
if (string.startsWith(prefix)) {
return true;
}
}
return false;
}
/**
* @param context the <code>FacesContext</code> for the current request
* @return the Locale from the UIViewRoot, the the value of Locale.getDefault()
*/
public static Locale getLocaleFromContextOrSystem(FacesContext context) {
Locale result, temp = Locale.getDefault();
UIViewRoot root;
result = temp;
if (null != context && null != (root = context.getViewRoot()) && null == (result = root.getLocale())) {
result = temp;
}
return result;
}
public static Converter getConverterForClass(Class converterClass,
FacesContext context) {
if (converterClass == null) {
return null;
}
try {
Application application = context.getApplication();
return (application.createConverter(converterClass));
} catch (Exception e) {
return (null);
}
}
public static Converter getConverterForIdentifer(String converterId,
FacesContext context) {
if (converterId == null) {
return null;
}
try {
Application application = context.getApplication();
return (application.createConverter(converterId));
} catch (Exception e) {
return (null);
}
}
public static StateManager getStateManager(FacesContext context)
throws FacesException {
return (context.getApplication().getStateManager());
}
public static Class getTypeFromString(String type) throws ClassNotFoundException {
Class result;
switch (type) {
case "byte":
result = Byte.TYPE;
break;
case "short":
result = Short.TYPE;
break;
case "int":
result = Integer.TYPE;
break;
case "long":
result = Long.TYPE;
break;
case "float":
result = Float.TYPE;
break;
case "double":
result = Double.TYPE;
break;
case "boolean":
result = Boolean.TYPE;
break;
case "char":
result = Character.TYPE;
break;
case "void":
result = Void.TYPE;
break;
default:
if (type.indexOf('.') == -1) {
type = "java.lang." + type;
} result = Util.loadClass(type, Void.TYPE);
break;
}
return result;
}
public static ViewHandler getViewHandler(FacesContext context)
throws FacesException {
// Get Application instance
Application application = context.getApplication();
assert (application != null);
// Get the ViewHandler
ViewHandler viewHandler = application.getViewHandler();
assert (viewHandler != null);
return viewHandler;
}
public static boolean componentIsDisabled(UIComponent component) {
return (Boolean.valueOf(String.valueOf(component.getAttributes().get("disabled"))));
}
public static boolean componentIsDisabledOrReadonly(UIComponent component) {
return Boolean.valueOf(String.valueOf(component.getAttributes().get("disabled"))) || Boolean.valueOf(String.valueOf(component.getAttributes().get("readonly")));
}
// W3C XML specification refers to IETF RFC 1766 for language code
// structure, therefore the value for the xml:lang attribute should
// be in the form of language or language-country or
// language-country-variant.
public static Locale getLocaleFromString(String localeStr)
throws IllegalArgumentException {
// length must be at least 2.
if (null == localeStr || localeStr.length() < 2) {
throw new IllegalArgumentException("Illegal locale String: " +
localeStr);
}
Locale result = null;
try {
Method method = Locale.class.getMethod("forLanguageTag", String.class);
if (method != null) {
result = (Locale) method.invoke(null, localeStr);
}
} catch(NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException throwable) {
// if we are NOT running JavaSE 7 we end up here and we will
// default to the previous way of determining the Locale below.
}
if (result == null || result.getLanguage().equals("")) {
String lang = null;
String country = null;
String variant = null;
char[] seps = {
'-',
'_'
};
int inputLength = localeStr.length();
int i = 0;
int j = 0;
// to have a language, the length must be >= 2
if ((inputLength >= 2) &&
((i = indexOfSet(localeStr, seps, 0)) == -1)) {
// we have only Language, no country or variant
if (2 != localeStr.length()) {
throw new
IllegalArgumentException("Illegal locale String: " +
localeStr);
}
lang = localeStr.toLowerCase();
}
// we have a separator, it must be either '-' or '_'
if (i != -1) {
lang = localeStr.substring(0, i);
// look for the country sep.
// to have a country, the length must be >= 5
if ((inputLength >= 5) &&
((j = indexOfSet(localeStr, seps, i + 1)) == -1)) {
// no further separators, length must be 5
if (inputLength != 5) {
throw new
IllegalArgumentException("Illegal locale String: " +
localeStr);
}
country = localeStr.substring(i + 1);
}
if (j != -1) {
country = localeStr.substring(i + 1, j);
// if we have enough separators for language, locale,
// and variant, the length must be >= 8.
if (inputLength >= 8) {
variant = localeStr.substring(j + 1);
} else {
throw new
IllegalArgumentException("Illegal locale String: " +
localeStr);
}
}
}
if (variant != null && country != null && lang != null) {
result = new Locale(lang, country, variant);
} else if (lang != null && country != null) {
result = new Locale(lang, country);
} else if (lang != null) {
result = new Locale(lang, "");
}
}
return result;
}
/**
* @param str local string
* @param set the substring
* @param fromIndex starting index
* @return starting at <code>fromIndex</code>, the index of the
* first occurrence of any substring from <code>set</code> in
* <code>toSearch</code>, or -1 if no such match is found
*/
public static int indexOfSet(String str, char[] set, int fromIndex) {
int result = -1;
for (int i = fromIndex, len = str.length(); i < len; i++) {
for (int j = 0, innerLen = set.length; j < innerLen; j++) {
if (str.charAt(i) == set[j]) {
result = i;
break;
}
}
if (-1 != result) {
break;
}
}
return result;
}
/**
* <p>Leverage the Throwable.getStackTrace() method to produce a String
* version of the stack trace, with a "\n" before each line.</p>
*
* @param e the Throwable to obtain the stacktrace from
*
* @return the String representation ofthe stack trace obtained by calling
* getStackTrace() on the passed in exception. If null is passed
* in, we return the empty String.
*/
public static String getStackTraceString(Throwable e) {
if (null == e) {
return "";
}
StackTraceElement[] stacks = e.getStackTrace();
StringBuffer sb = new StringBuffer();
for (StackTraceElement stack : stacks) {
sb.append(stack.toString()).append('\n');
}
return sb.toString();
}
/**
* <p>PRECONDITION: argument <code>response</code> is non-null and
* has a method called <code>getContentType</code> that takes no
* arguments and returns a String, with no side-effects.</p>
*
* <p>This method allows us to get the contentType in both the
* servlet and portlet cases, without introducing a compile-time
* dependency on the portlet api.</p>
*
* @param response the current response
* @return the content type of the response
*/
public static String getContentTypeFromResponse(Object response) {
String result = null;
if (null != response) {
try {
Method method = ReflectionUtils.lookupMethod(
response.getClass(),
"getContentType",
RIConstants.EMPTY_CLASS_ARGS
);
if (null != method) {
Object obj =
method.invoke(response, RIConstants.EMPTY_METH_ARGS);
if (null != obj) {
result = obj.toString();
}
}
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new FacesException(e);
}
}
return result;
}
public static FeatureDescriptor getFeatureDescriptor(String name, String
displayName, String desc, boolean expert, boolean hidden,
boolean preferred, Object type, Boolean designTime) {
FeatureDescriptor fd = new FeatureDescriptor();
fd.setName(name);
fd.setDisplayName(displayName);
fd.setShortDescription(desc);
fd.setExpert(expert);
fd.setHidden(hidden);
fd.setPreferred(preferred);
fd.setValue(ELResolver.TYPE, type);
fd.setValue(ELResolver.RESOLVABLE_AT_DESIGN_TIME, designTime);
return fd;
}
/**
* <p>A slightly more efficient version of
* <code>String.split()</code> which caches
* the <code>Pattern</code>s in an LRUMap instead of
* creating a new <code>Pattern</code> on each
* invocation.</p>
* @param appMap the Application Map
* @param toSplit the string to split
* @param regex the regex used for splitting
* @return the result of <code>Pattern.spit(String, int)</code>
*/
public synchronized static String[] split(Map<String, Object> appMap, String toSplit, String regex) {
Map<String, Pattern> patternCache = getPatternCache(appMap);
Pattern pattern = patternCache.get(regex);
if (pattern == null) {
pattern = Pattern.compile(regex);
patternCache.put(regex, pattern);
}
return pattern.split(toSplit, 0);
}
public synchronized static String[] split(ServletContext sc,
String toSplit, String regex) {
Map<String, Pattern> patternCache = getPatternCache(sc);
Pattern pattern = patternCache.get(regex);
if (pattern == null) {
pattern = Pattern.compile(regex);
patternCache.put(regex, pattern);
}
return pattern.split(toSplit, 0);
}
/**
* <p>Returns the URL pattern of the
* {@link javax.faces.webapp.FacesServlet} that
* is executing the current request. If there are multiple
* URL patterns, the value returned by
* <code>HttpServletRequest.getServletPath()</code> and
* <code>HttpServletRequest.getPathInfo()</code> is
* used to determine which mapping to return.</p>
* If no mapping can be determined, it most likely means
* that this particular request wasn't dispatched through
* the {@link javax.faces.webapp.FacesServlet}.
* <p>
*
* <b>NOTE:</b> This method was supposed to be replaced with the "mapping API"
* from Servlet 4, but this has not been implemented in time for JSF 2.3
* to depend on.
*
* @param context the {@link FacesContext} of the current request
*
* @return the URL pattern of the {@link javax.faces.webapp.FacesServlet}
* or <code>null</code> if no mapping can be determined
*
* @throws NullPointerException if <code>context</code> is null
*/
public static String getFacesMapping(FacesContext context) {
notNull("context", context);
// Check for a previously stored mapping
String mapping = (String) RequestStateManager.get(context, INVOCATION_PATH);
if (mapping == null) {
// First check for javax.servlet.forward.servlet_path
// and javax.servlet.forward.path_info for non-null
// values. If either is non-null, use this
// information to generate determine the mapping.
ExternalContext externalContext = context.getExternalContext();
String servletPath = externalContext.getRequestServletPath();
String pathInfo = externalContext.getRequestPathInfo();
mapping = getMappingForRequest(externalContext, servletPath, pathInfo);
if (mapping == null && LOGGER.isLoggable(FINE)) {
LOGGER.log(FINE,
"jsf.faces_servlet_mapping_cannot_be_determined_error",
new Object[]{servletPath});
}
if (mapping != null) {
RequestStateManager.set(context, INVOCATION_PATH, mapping);
}
}
if (LOGGER.isLoggable(FINE)) {
LOGGER.log(FINE, "URL pattern of the FacesServlet executing the current request " + mapping);
}
return mapping;
}
/**
* <p>Return the appropriate {@link javax.faces.webapp.FacesServlet} mapping
* based on the servlet path of the current request.</p>
*
* @param externalContext the external context of the request
* @param servletPath the servlet path of the request
* @param pathInfo the path info of the request
*
* @return the appropriate mapping based on the current request
*
* @see javax.servlet.http.HttpServletRequest#getServletPath()
*/
private static String getMappingForRequest(ExternalContext externalContext, String servletPath, String pathInfo) {
if (servletPath == null) {
return null;
}
if (LOGGER.isLoggable(FINE)) {
LOGGER.log(FINE, "servletPath " + servletPath);
LOGGER.log(FINE, "pathInfo " + pathInfo);
}
// If the path returned by HttpServletRequest.getServletPath()
// returns a zero-length String, then the FacesServlet has
// been mapped to '/*'.
if (servletPath.length() == 0) {
return "/*";
}
// Presence of path info means we were invoked using a prefix path mapping
if (pathInfo != null) {
return servletPath;
} else if (servletPath.indexOf('.') < 0) {
// If pathInfo is null and no '.' is present, the FacesServlet
// could be invoked using prefix path but without any pathInfo -
// i.e. GET /contextroot/faces or GET /contextroot/faces/
//
// It could also be that the FacesServlet is invoked using an
// exact mapping, i.e. GET /contextroot/foo
// Look at the Servlet mappings to see which case we have here:
Object context = externalContext.getContext();
if (context instanceof ServletContext) {
Collection<String> servletMappings = getFacesServletMappings((ServletContext) context);
if (servletMappings.contains(servletPath)) {
return addExactMappedMarker(servletPath); // It's not ideal, to be replaced by Servlet 4 mapping API types
}
}
return servletPath;
} else {
// Servlet invoked using extension mapping
return servletPath.substring(servletPath.lastIndexOf('.'));
}
}
/**
* Checks if the FacesServlet is exact mapped to the given resource.
* <p>
* Not to be confused with {@link Util#isExactMapped(String)}, which checks
* if a string representing a mapping, not a resource, is an exact mapping.
* <p>
* This should be replaced by the Servlet 4 mapping API when/if that becomes available
* and JSF/Mojarra can depend on it.
*
* @param viewId the view id to test
* @return true if the FacesServlet is exact mapped to the given viewId, false otherwise
*/
public static boolean isViewIdExactMappedToFacesServlet(String viewId) {
return isResourceExactMappedToFacesServlet(FacesContext.getCurrentInstance().getExternalContext(), viewId);
}
/**
* Checks if the FacesServlet is exact mapped to the given resource.
* <p>
* Not to be confused with {@link Util#isExactMapped(String)}, which checks
* if a string representing a mapping, not a resource, is an exact mapping.
* <p>
* This should be replaced by the Servlet 4 mapping API when/if that becomes available
* and JSF/Mojarra can depend on it.
*
* @param externalContext the external context for this request
* @param resource the resource to test
* @return true if the FacesServlet is exact mapped to the given resource, false otherwise
*/
public static boolean isResourceExactMappedToFacesServlet(ExternalContext externalContext, String resource) {
Object context = externalContext.getContext();
if (context instanceof ServletContext) {
return getFacesServletMappings((ServletContext) context).contains(resource);
}
return false;
}
public static String getFirstWildCardMappingToFacesServlet(ExternalContext externalContext) {
// If needed, cache this after initialization of JSF
Object context = externalContext.getContext();
if (context instanceof ServletContext) {
return getFacesServletMappings((ServletContext) context)
.stream()
.filter(mapping -> mapping.contains("*"))
.findFirst()
.orElse(null);
}
return null;
}
private static final String EXACT_MARKER = "* *";
public static String addExactMappedMarker(String mapping) {
return EXACT_MARKER + mapping;
}
public static String removeExactMappedMarker(String mapping) {
return mapping.substring(EXACT_MARKER.length());
}
/**
* <p>Returns true if the provided <code>url-mapping</code> is
* an exact mapping (starts with {@link Util#EXACT_MARKER}).</p>
*
* @param mapping a <code>url-pattern</code>
* @return true if the mapping starts with <code>/</code>
*/
public static boolean isExactMapped(String mapping) {
return mapping.startsWith("* *");
}
/**
* <p>Returns true if the provided <code>url-mapping</code> is
* a prefix path mapping (starts with <code>/</code>).</p>
*
* @param mapping a <code>url-pattern</code>
* @return true if the mapping starts with <code>/</code>
*/
public static boolean isPrefixMapped(String mapping) {
return mapping.charAt(0) == '/';
}
public static boolean isSpecialAttributeName(String name) {
boolean isSpecialAttributeName = name.equals("action") ||
name.equals("actionListener") || name.equals("validator")
|| name.equals("valueChangeListener");
return isSpecialAttributeName;
}
/**
* @param ctx the {@link FacesContext} for the current request
* @param viewToRender the {@link UIViewRoot} to check
* @return <code>true</code> if the {@link FacesContext} attributes map
* contains a reference to the {@link UIViewRoot}'s view ID
*/
public static boolean isViewPopulated(FacesContext ctx, UIViewRoot viewToRender) {
return ctx.getAttributes().containsKey(viewToRender);
}
/**
* <p>
* Flag the specified {@link UIViewRoot} as populated.
* </p>
* @param ctx the {@link FacesContext} for the current request
* @param viewToRender the {@link UIViewRoot} to mark as populated
*/
public static void setViewPopulated(FacesContext ctx,
UIViewRoot viewToRender) {
ctx.getAttributes().put(viewToRender, Boolean.TRUE);
}
/**
* Utility method to validate ID uniqueness for the tree represented
* by <code>component</code>.
*/
public static void checkIdUniqueness(FacesContext context,
UIComponent component,
Set<String> componentIds) {
boolean uniquenessCheckDisabled = false;
if (context.isProjectStage(ProjectStage.Production)) {
WebConfiguration config = WebConfiguration.getInstance(context.getExternalContext());
uniquenessCheckDisabled = config.isOptionEnabled(
WebConfiguration.BooleanWebContextInitParameter.DisableIdUniquenessCheck);
}
if (!uniquenessCheckDisabled) {
// deal with children/facets that are marked transient.
for (Iterator<UIComponent> kids = component.getFacetsAndChildren();
kids.hasNext();) {
UIComponent kid = kids.next();
// check for id uniqueness
String id = kid.getClientId(context);
if (componentIds.add(id)) {
checkIdUniqueness(context, kid, componentIds);
} else {
if (LOGGER.isLoggable(Level.SEVERE)) {
LOGGER.log(Level.SEVERE,
"jsf.duplicate_component_id_error",
id);
FastStringWriter writer = new FastStringWriter(128);
DebugUtil.simplePrintTree(context.getViewRoot(), id, writer);
LOGGER.severe(writer.toString());
}
String message =
MessageUtils.getExceptionMessageString(
MessageUtils.DUPLICATE_COMPONENT_ID_ERROR_ID, id);
throw new IllegalStateException(message);
}
}
}
}
static public boolean classHasAnnotations(Class<?> clazz) {
if (clazz != null) {
while (clazz != Object.class) {
Field[] fields = clazz.getDeclaredFields();
if (fields != null) {
for (Field field : fields) {
if (field.getAnnotations().length > 0) {
return true;
}
}
}
Method[] methods = clazz.getDeclaredMethods();
if (methods != null) {
for (Method method : methods) {
if (method.getDeclaredAnnotations().length > 0) {
return true;
}
}
}
clazz = clazz.getSuperclass();
}
}
return false;
}
/**
* If view root is instance of naming container, return its container client id, suffixed with separator character.
* @param context Involved faces context.
* @return The naming container prefix, or an empty string if the view root is not an instance of naming container.
*/
public static String getNamingContainerPrefix(FacesContext context) {
UIViewRoot viewRoot = context.getViewRoot();
if (viewRoot == null) {
Application application = context.getApplication();
viewRoot = (UIViewRoot) application.createComponent(UIViewRoot.COMPONENT_TYPE);
}
if (viewRoot instanceof NamingContainer) {
return viewRoot.getContainerClientId(context) + UINamingContainer.getSeparatorChar(context);
} else {
return "";
}
}
public static String getViewStateId(FacesContext context) {
String result = null;
final String viewStateCounterKey = "com.sun.faces.util.ViewStateCounterKey";
Map<Object, Object> contextAttrs = context.getAttributes();
Integer counter = (Integer) contextAttrs.get(viewStateCounterKey);
if (null == counter) {
counter = Integer.valueOf(0);
}
char sep = UINamingContainer.getSeparatorChar(context);
UIViewRoot root = context.getViewRoot();
result = root.getContainerClientId(context) + sep +
ResponseStateManager.VIEW_STATE_PARAM + sep +
+ counter;
contextAttrs.put(viewStateCounterKey, ++counter);
return result;
}
public static String getClientWindowId(FacesContext context) {
String result = null;
final String clientWindowIdCounterKey = "com.sun.faces.util.ClientWindowCounterKey";
Map<Object, Object> contextAttrs = context.getAttributes();
Integer counter = (Integer) contextAttrs.get(clientWindowIdCounterKey);
if (null == counter) {
counter = Integer.valueOf(0);
}
char sep = UINamingContainer.getSeparatorChar(context);
result = context.getViewRoot().getContainerClientId(context) + sep +
ResponseStateManager.CLIENT_WINDOW_PARAM + sep + counter;
contextAttrs.put(clientWindowIdCounterKey, ++counter);
return result;
}
private static final String FACES_CONTEXT_ATTRIBUTES_DOCTYPE_KEY = Util.class.getName() + "_FACES_CONTEXT_ATTRS_DOCTYPE_KEY";
public static void saveDOCTYPEToFacesContextAttributes(String DOCTYPE) {
FacesContext context = FacesContext.getCurrentInstance();
if (null == context) {
return;
}
Map<Object, Object> attrs = context.getAttributes();
attrs.put(FACES_CONTEXT_ATTRIBUTES_DOCTYPE_KEY, DOCTYPE);
}
public static String getDOCTYPEFromFacesContextAttributes(FacesContext context) {
if (null == context) {
return null;
}
Map<Object, Object> attrs = context.getAttributes();
return (String) attrs.get(FACES_CONTEXT_ATTRIBUTES_DOCTYPE_KEY);
}
private static final String FACES_CONTEXT_ATTRIBUTES_XMLDECL_KEY = Util.class.getName() + "_FACES_CONTEXT_ATTRS_XMLDECL_KEY";
public static void saveXMLDECLToFacesContextAttributes(String XMLDECL) {
FacesContext context = FacesContext.getCurrentInstance();
if (null == context) {
return;
}
Map<Object, Object> attrs = context.getAttributes();
attrs.put(FACES_CONTEXT_ATTRIBUTES_XMLDECL_KEY, XMLDECL);
}
public static String getXMLDECLFromFacesContextAttributes(FacesContext context) {
if (null == context) {
return null;
}
Map<Object, Object> attrs = context.getAttributes();
return (String) attrs.get(FACES_CONTEXT_ATTRIBUTES_XMLDECL_KEY);
}
public static long getLastModified(URL url) {
long lastModified;
URLConnection conn;
InputStream is = null;
try {
conn = url.openConnection();
if (conn instanceof JarURLConnection) {
/*
* Note this is a work around for JarURLConnection since the
* getLastModified method is buggy. See JAVASERVERFACES-2725
* and JAVASERVERFACES-2734.
*/
JarURLConnection jarUrlConnection = (JarURLConnection) conn;
URL jarFileUrl = jarUrlConnection.getJarFileURL();
URLConnection jarFileConnection = jarFileUrl.openConnection();
lastModified = jarFileConnection.getLastModified();
jarFileConnection.getInputStream().close();
}
else {
is = conn.getInputStream();
lastModified = conn.getLastModified();
}
} catch (Exception e) {
throw new FacesException("Error Checking Last Modified for " + url, e);
} finally {
if (is != null) {
try {
is.close();
} catch (Exception e) {
if (LOGGER.isLoggable(Level.FINEST)) {
LOGGER.log(Level.FINEST, "Closing stream", e);
}
}
}
}
return lastModified;
}
/**
* Get the faces-config.xml version (if any).
*
* @param facesContext the Faces context.
* @return the version found, or "" if none found.
*/
public static String getFacesConfigXmlVersion(FacesContext facesContext) {
String result = "";
InputStream stream = null;
try {
URL url = facesContext.getExternalContext().getResource("/WEB-INF/faces-config.xml");
if (url != null) {
XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
xpath.setNamespaceContext(new JavaeeNamespaceContext());
stream = url.openStream();
result = xpath.evaluate("string(/javaee:faces-config/@version)", new InputSource(stream));
}
} catch (MalformedURLException mue) {
} catch (XPathExpressionException | IOException xpee) {
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException ioe) {
}
}
}
return result;
}
/**
* Get the web.xml version (if any).
*
* @param facesContext the Faces context.
* @return the version found, or "" if none found.
*/
public static String getWebXmlVersion(FacesContext facesContext) {
String result = "";
InputStream stream = null;
try {
URL url = facesContext.getExternalContext().getResource("/WEB-INF/web.xml");
if (url != null) {
XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
xpath.setNamespaceContext(new JavaeeNamespaceContext());
stream = url.openStream();
result = xpath.evaluate("string(/javaee:web-app/@version)", new InputSource(stream));
}
} catch (MalformedURLException mue) {
} catch (XPathExpressionException | IOException xpee) {
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException ioe) {
}
}
}
return result;
}
public static class JavaeeNamespaceContext implements NamespaceContext {
@Override
public String getNamespaceURI(String prefix) {
return "http://xmlns.jcp.org/xml/ns/javaee";
}
@Override
public String getPrefix(String namespaceURI) {
return "javaee";
}
@Override
public Iterator getPrefixes(String namespaceURI) {
return null;
}
}
/**
* Get the CDI bean manager.
*
* @param facesContext the Faces context to consult
* @return the CDI bean manager.
*/
public static BeanManager getCdiBeanManager(FacesContext facesContext) {
BeanManager result = null;
if (facesContext != null && facesContext.getAttributes().containsKey(RIConstants.CDI_BEAN_MANAGER)) {
result = (BeanManager) facesContext.getAttributes().get(RIConstants.CDI_BEAN_MANAGER);
} else if (facesContext != null && facesContext.getExternalContext().getApplicationMap().containsKey(RIConstants.CDI_BEAN_MANAGER)) {
result = (BeanManager) facesContext.getExternalContext().getApplicationMap().get(RIConstants.CDI_BEAN_MANAGER);
} else {
try {
InitialContext initialContext = new InitialContext();
result = (BeanManager) initialContext.lookup("java:comp/BeanManager");
}
catch (NamingException ne) {
try {
InitialContext initialContext = new InitialContext();
result = (BeanManager) initialContext.lookup("java:comp/env/BeanManager");
}
catch (NamingException ne2) {
}
}
if (result == null && facesContext != null) {
Map<String, Object> applicationMap = facesContext.getExternalContext().getApplicationMap();
result = (BeanManager) applicationMap.get("org.jboss.weld.environment.servlet.javax.enterprise.inject.spi.BeanManager");
}
if (result != null && facesContext != null) {
facesContext.getAttributes().put(RIConstants.CDI_BEAN_MANAGER, result);
facesContext.getExternalContext().getApplicationMap().put(RIConstants.CDI_BEAN_MANAGER, result);
}
}
return result;
}
/**
* Is CDI available.
*
* @param facesContext the Faces context to consult.
* @return true if available, false otherwise.
*/
public static boolean isCdiAvailable(FacesContext facesContext) {
boolean result;
if (facesContext != null && facesContext.getAttributes().containsKey(RIConstants.CDI_AVAILABLE)) {
result = (Boolean) facesContext.getAttributes().get(RIConstants.CDI_AVAILABLE);
} else if (facesContext != null && facesContext.getExternalContext().getApplicationMap().containsKey(RIConstants.CDI_AVAILABLE)) {
result = (Boolean) facesContext.getExternalContext().getApplicationMap().get(RIConstants.CDI_AVAILABLE);
} else {
result = getCdiBeanManager(facesContext) != null;
if (result && facesContext != null) {
facesContext.getAttributes().put(RIConstants.CDI_AVAILABLE, result);
facesContext.getExternalContext().getApplicationMap().put(RIConstants.CDI_AVAILABLE, result);
}
}
return result;
}
/**
* Is CDI available (ServletContext variant)
*
* @param servletContext the servlet context.
* @return true if available, false otherwise.
*/
public static boolean isCdiAvailable(ServletContext servletContext) {
boolean result;
Object value = servletContext.getAttribute(RIConstants.CDI_AVAILABLE);
if (value != null) {
result = (Boolean) value;
} else {
result = getCdiBeanManager(null) != null;
if (result) {
servletContext.setAttribute(RIConstants.CDI_AVAILABLE, result);
}
}
return result;
}
/**
* Is CDI 1.1 or later
*
* @param facesContext the Faces context.
* @return true if CDI 1.1 or later, false otherwise.
*/
public static boolean isCdiOneOneOrLater(FacesContext facesContext) {
boolean result = false;
if (facesContext != null && facesContext.getAttributes().containsKey(RIConstants.CDI_1_1_OR_LATER)) {
result = (Boolean) facesContext.getAttributes().get(RIConstants.CDI_1_1_OR_LATER);
} else if (facesContext != null && facesContext.getExternalContext().getApplicationMap().containsKey(RIConstants.CDI_1_1_OR_LATER)) {
result = facesContext.getExternalContext().getApplicationMap().containsKey(RIConstants.CDI_1_1_OR_LATER);
} else {
try {
Class.forName("javax.enterprise.context.Initialized");
result = true;
} catch (ClassNotFoundException ignored) {
if (LOGGER.isLoggable(Level.FINEST)) {
LOGGER.log(Level.FINEST, "Detected CDI 1.0", ignored);
}
}
if (facesContext != null) {
facesContext.getAttributes().put(RIConstants.CDI_1_1_OR_LATER, result);
facesContext.getExternalContext().getApplicationMap().put(RIConstants.CDI_1_1_OR_LATER, result);
}
}
return result;
}
}