/**
* 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.tomee.myfaces;
import org.apache.myfaces.config.DefaultFacesConfigResourceProvider;
import org.apache.myfaces.shared.util.ClassUtils;
import org.apache.openejb.config.NewLoaderLogic;
import org.apache.openejb.loader.Files;
import org.apache.openejb.util.AppFinder;
import org.apache.openejb.util.URLs;
import org.apache.xbean.finder.UrlSet;
import javax.faces.context.ExternalContext;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class TomEEFacesConfigResourceProvider extends DefaultFacesConfigResourceProvider {
private static final String META_INF_PREFIX = "META-INF/";
private static final String FACES_CONFIG_SUFFIX = ".faces-config.xml";
private static final String FACES_CONFIG_IMPLICIT = "META-INF/faces-config.xml";
private static final Map<ClassLoader, Collection<URL>> CACHED_RESOURCES = new HashMap<>();
@Override
public Collection<URL> getMetaInfConfigurationResources(final ExternalContext notUsedNullIsPassedFromInitializer) throws IOException {
final ClassLoader loader = getClassLoader();
Collection<URL> urlSet = CACHED_RESOURCES.get(loader);
if (urlSet != null) {
return new HashSet<>(urlSet); // copy it since it can be modified then
}
urlSet = new HashSet<>();
final Enumeration<URL> resources = loader.getResources(FACES_CONFIG_IMPLICIT);
while (resources.hasMoreElements()) {
urlSet.add(resources.nextElement());
}
final List<URL> urls = NewLoaderLogic.applyBuiltinExcludes(new UrlSet(loader)).getUrls();
final ExecutorService es = Executors.newFixedThreadPool(2 * Runtime.getRuntime().availableProcessors() + 1);
final Collection<Future<Set<URL>>> futures = new ArrayList<>(urls.size());
// Scan files inside META-INF ending with .faces-config.xml
for (final URL url : urls) {
final File file = URLs.toFile(url);
if (!file.exists()) {
continue;
}
futures.add(es.submit(new Callable<Set<URL>>() {
@Override
public Set<URL> call() throws Exception {
final Set<URL> currentSet = new HashSet<>();
if (!file.isDirectory()) { // browse all entries to see if we have a matching file
final Enumeration<JarEntry> e = new JarFile(file).entries();
while (e.hasMoreElements()) {
try {
final String name = e.nextElement().getName();
if (name.startsWith(META_INF_PREFIX) && name.endsWith(FACES_CONFIG_SUFFIX)) {
final Enumeration<URL> e2 = loader.getResources(name);
while (e2.hasMoreElements()) {
currentSet.add(e2.nextElement());
}
}
} catch (final Throwable ignored) {
// no-op
}
}
} else {
final File metaInf = new File(file, META_INF_PREFIX);
if (metaInf.exists() && metaInf.isDirectory()) {
for (final File f : Files.collect(metaInf, FacesConfigSuffixFilter.INSTANCE)) {
if (!f.isDirectory()) {
currentSet.add(f.toURI().toURL());
}
}
}
}
return currentSet;
}
}));
}
es.shutdown();
for (final Future<Set<URL>> set : futures) {
try {
urlSet.addAll(set.get());
} catch (final Exception e) {
// no-op
}
}
try {
if (AppFinder.findAppContextOrWeb(
Thread.currentThread().getContextClassLoader(), AppFinder.WebBeansContextTransformer.INSTANCE) == null) {
final Iterator<URL> toFilter = urlSet.iterator();
while (toFilter.hasNext()) {
final URL url = toFilter.next();
if (TomEEMyFacesContainerInitializer.isOwb(url)) {
toFilter.remove();
}
}
}
} catch (final Throwable th) {
// no-op
}
CACHED_RESOURCES.put(loader, urlSet);
return new HashSet<>(urlSet);
}
private ClassLoader getClassLoader() {
ClassLoader loader = ClassUtils.getContextClassLoader();
if (loader == null) {
loader = this.getClass().getClassLoader();
}
return loader;
}
public static void clear(final ClassLoader loader) {
CACHED_RESOURCES.remove(loader);
}
private static class FacesConfigSuffixFilter implements FileFilter {
public static final FacesConfigSuffixFilter INSTANCE = new FacesConfigSuffixFilter();
@Override
public boolean accept(final File pathname) {
return pathname.isDirectory() || pathname.getName().endsWith(FACES_CONFIG_SUFFIX);
}
}
}