/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.portal.osgi.web.servlet.jsp.compiler.internal; import com.liferay.portal.kernel.concurrent.ConcurrentReferenceValueHashMap; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.memory.FinalizeManager; import com.liferay.portal.kernel.util.CharPool; import com.liferay.portal.kernel.util.StringPool; import com.liferay.portal.osgi.web.servlet.jsp.compiler.internal.util.ClassPathUtil; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.nio.file.DirectoryStream; import java.nio.file.DirectoryStream.Filter; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Path; import java.nio.file.spi.FileSystemProvider; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javax.tools.JavaFileObject; import org.osgi.framework.Bundle; import org.osgi.framework.wiring.BundleCapability; import org.osgi.framework.wiring.BundleWiring; import org.osgi.util.tracker.ServiceTracker; /** * @author Raymond Augé */ public class JspJavaFileObjectResolver implements JavaFileObjectResolver { public JspJavaFileObjectResolver( BundleWiring bundleWiring, BundleWiring jspBundleWiring, Map<BundleWiring, Set<String>> bundleWiringPackageNames, ServiceTracker<Map<String, List<URL>>, Map<String, List<URL>>> serviceTracker) { _bundleWiring = bundleWiring; _jspBundleWiring = jspBundleWiring; _bundleWiringPackageNames = bundleWiringPackageNames; _serviceTracker = serviceTracker; } @Override public Collection<JavaFileObject> resolveClasses( boolean recurse, String packagePath) { List<JavaFileObject> javaFileObjects = new ArrayList<>(); int options = 0; if (recurse) { options = BundleWiring.LISTRESOURCES_RECURSE; } javaFileObjects.addAll( toJavaFileObjects( _jspBundleWiring.getBundle(), _jspBundleWiring.listResources( packagePath, "*.class", options))); String packageName = packagePath.replace( CharPool.SLASH, CharPool.PERIOD); for (Entry<BundleWiring, Set<String>> entry : _bundleWiringPackageNames.entrySet()) { Set<String> packageNames = entry.getValue(); if (packageNames.contains(packageName)) { javaFileObjects.addAll( doResolveClasses(entry.getKey(), packagePath, options)); } } if (javaFileObjects.isEmpty()) { return toJavaFileObjects( _bundleWiring.getBundle(), _bundleWiring.listResources(packagePath, "*.class", options)); } return javaFileObjects; } protected Collection<JavaFileObject> doResolveClasses( BundleWiring bundleWiring, String path, int options) { Bundle bundle = bundleWiring.getBundle(); if (bundle.getBundleId() == 0) { return handleSystemBundle(bundleWiring, path); } return toJavaFileObjects( bundle, bundleWiring.listResources(path, "*.class", options)); } protected String getClassName(String classResourceName) { classResourceName = classResourceName.substring( 0, classResourceName.length() - 6); return classResourceName.replace(CharPool.SLASH, CharPool.PERIOD); } protected JavaFileObject getJavaFileObject( URL resourceURL, String resourceName) { String protocol = resourceURL.getProtocol(); String className = getClassName(resourceName); if (protocol.equals("bundle") || protocol.equals("bundleresource")) { return new BundleJavaFileObject(className, resourceURL); } else if (protocol.equals("jar")) { try { return new JarJavaFileObject( className, ClassPathUtil.getFile(resourceURL), resourceName); } catch (IOException ioe) { _log.error(ioe.getMessage(), ioe); } } else if (protocol.equals("vfs")) { try { return new VfsJavaFileObject( className, resourceURL, resourceName); } catch (MalformedURLException murle) { _log.error(murle.getMessage(), murle); } } return null; } protected Collection<JavaFileObject> handleSystemBundle( BundleWiring bundleWiring, String path) { Collection<JavaFileObject> javaFileObjects = _javaFileObjects.get(path); if (javaFileObjects != null) { return javaFileObjects; } List<URL> urls = null; Map<String, List<URL>> extraPackageMap = _serviceTracker.getService(); if (extraPackageMap != null) { urls = extraPackageMap.get(path.replace('/', '.')); } if ((urls == null) || urls.isEmpty()) { ClassLoader classLoader = bundleWiring.getClassLoader(); try { Enumeration<URL> enumeration = classLoader.getResources(path); if ((enumeration == null) || ((enumeration != null) && !enumeration.hasMoreElements())) { // This is a fallback so that WebSphere can find resources // during the JSP compilation process enumeration = classLoader.getResources( path + StringPool.SLASH); } if ((enumeration != null) && enumeration.hasMoreElements()) { urls = Collections.list(enumeration); } } catch (IOException ioe) { _log.error(ioe.getMessage(), ioe); } } if ((urls == null) || urls.isEmpty()) { _javaFileObjects.put(path, Collections.<JavaFileObject>emptyList()); return Collections.emptyList(); } for (URL url : urls) { try { File file = ClassPathUtil.getFile(url); if (file == null) { if (_log.isWarnEnabled()) { _log.warn( "Ignoring " + url + " while handling system bundle"); } continue; } try (FileSystem fileSystem = FileSystems.newFileSystem( file.toPath(), null)) { FileSystemProvider fileSystemProvider = fileSystem.provider(); try (DirectoryStream<Path> directoryStream = fileSystemProvider.newDirectoryStream( fileSystem.getPath(path), new Filter<Path>() { @Override public boolean accept(Path entryPath) { String entryPathString = entryPath.toString(); return entryPathString.endsWith( ".class"); } })) { for (Path entryPath : directoryStream) { if (javaFileObjects == null) { javaFileObjects = new ArrayList<>(); } String entryPathString = entryPath.toString(); entryPathString = entryPathString.substring(1); javaFileObjects.add( new JarJavaFileObject( getClassName(entryPathString), file, entryPathString)); } } } } catch (IOException ioe) { _log.error(ioe.getMessage(), ioe); } } if (javaFileObjects == null) { javaFileObjects = Collections.<JavaFileObject>emptyList(); } _javaFileObjects.put(path, javaFileObjects); return javaFileObjects; } protected boolean isExportsPackage( BundleWiring bundleWiring, String packageName) { List<BundleCapability> bundleCapabilities = bundleWiring.getCapabilities("osgi.wiring.package"); for (BundleCapability bundleCapability : bundleCapabilities) { Map<String, Object> attributes = bundleCapability.getAttributes(); if (packageName.equals(attributes.get("osgi.wiring.package"))) { return true; } } return false; } protected Collection<JavaFileObject> toJavaFileObjects( Bundle bundle, Collection<String> resources) { if ((resources == null) || resources.isEmpty()) { return Collections.emptyList(); } List<JavaFileObject> javaFileObjects = new ArrayList<>( resources.size()); for (String resource : resources) { JavaFileObject javaFileObject = getJavaFileObject( bundle.getResource(resource), resource); if (javaFileObject != null) { javaFileObjects.add(javaFileObject); } } return javaFileObjects; } private static final Log _log = LogFactoryUtil.getLog( JspJavaFileObjectResolver.class); private final BundleWiring _bundleWiring; private final Map<BundleWiring, Set<String>> _bundleWiringPackageNames; private final Map<String, Collection<JavaFileObject>> _javaFileObjects = new ConcurrentReferenceValueHashMap<>( FinalizeManager.SOFT_REFERENCE_FACTORY); private final BundleWiring _jspBundleWiring; private final ServiceTracker<Map<String, List<URL>>, Map<String, List<URL>>> _serviceTracker; }