/** * 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.bootstrap; import aQute.bnd.header.OSGiHeader; import aQute.bnd.header.Parameters; import aQute.bnd.version.Version; import com.liferay.portal.kernel.concurrent.DefaultNoticeableFuture; import com.liferay.portal.kernel.exception.PortalException; import com.liferay.portal.kernel.io.unsync.UnsyncBufferedInputStream; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.lpkg.StaticLPKGResolver; import com.liferay.portal.kernel.module.framework.ThrowableCollector; import com.liferay.portal.kernel.security.auth.PrincipalException; import com.liferay.portal.kernel.security.permission.PermissionChecker; import com.liferay.portal.kernel.security.permission.PermissionThreadLocal; import com.liferay.portal.kernel.spring.osgi.OSGiBeanProperties; import com.liferay.portal.kernel.util.CharPool; import com.liferay.portal.kernel.util.FileUtil; import com.liferay.portal.kernel.util.HashMapDictionary; import com.liferay.portal.kernel.util.Props; import com.liferay.portal.kernel.util.PropsKeys; import com.liferay.portal.kernel.util.PropsUtil; import com.liferay.portal.kernel.util.ReflectionUtil; import com.liferay.portal.kernel.util.ReleaseInfo; import com.liferay.portal.kernel.util.ServiceLoader; import com.liferay.portal.kernel.util.ServiceLoaderCondition; import com.liferay.portal.kernel.util.StringBundler; import com.liferay.portal.kernel.util.StringPool; import com.liferay.portal.kernel.util.StringUtil; import com.liferay.portal.kernel.util.SystemProperties; import com.liferay.portal.module.framework.ModuleFramework; import com.liferay.portal.util.PropsValues; import com.liferay.registry.Registry; import com.liferay.registry.RegistryUtil; import com.liferay.registry.collections.ServiceTrackerMapFactory; import com.liferay.registry.collections.ServiceTrackerMapFactoryUtil; import com.liferay.registry.internal.RegistryImpl; import com.liferay.registry.internal.ServiceTrackerMapFactoryImpl; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.nio.file.DirectoryStream; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.security.CodeSource; import java.security.ProtectionDomain; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Dictionary; 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.Map.Entry; import java.util.Properties; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.jar.Attributes; import java.util.jar.JarInputStream; import java.util.jar.Manifest; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import javax.servlet.ServletContext; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleEvent; import org.osgi.framework.BundleException; import org.osgi.framework.Constants; import org.osgi.framework.FrameworkEvent; import org.osgi.framework.FrameworkListener; import org.osgi.framework.ServiceRegistration; import org.osgi.framework.launch.Framework; import org.osgi.framework.launch.FrameworkFactory; import org.osgi.framework.startlevel.BundleStartLevel; import org.osgi.framework.startlevel.FrameworkStartLevel; import org.osgi.framework.wiring.BundleRevision; import org.osgi.framework.wiring.FrameworkWiring; import org.osgi.util.tracker.BundleTracker; import org.springframework.beans.factory.BeanIsAbstractException; import org.springframework.context.ApplicationContext; /** * @author Raymond Augé * @author Miguel Pastor * @author Kamesh Sampath */ public class ModuleFrameworkImpl implements ModuleFramework { @Override public long addBundle(String location) throws PortalException { Bundle bundle = _addBundle(location, null, true); return bundle.getBundleId(); } @Override public long addBundle(String location, InputStream inputStream) throws PortalException { Bundle bundle = _addBundle(location, inputStream, true); return bundle.getBundleId(); } public Bundle getBundle( BundleContext bundleContext, InputStream inputStream) throws PortalException { try { JarInputStream jarInputStream = new JarInputStream(inputStream); Manifest manifest = jarInputStream.getManifest(); Attributes attributes = manifest.getMainAttributes(); String bundleSymbolicNameAttributeValue = attributes.getValue( Constants.BUNDLE_SYMBOLICNAME); Parameters parameters = OSGiHeader.parseHeader( bundleSymbolicNameAttributeValue); Set<String> set = parameters.keySet(); Iterator<String> iterator = set.iterator(); String bundleSymbolicName = iterator.next(); String bundleVersionAttributeValue = attributes.getValue( Constants.BUNDLE_VERSION); Version bundleVersion = Version.parseVersion( bundleVersionAttributeValue); for (Bundle bundle : bundleContext.getBundles()) { Version curBundleVersion = Version.parseVersion( String.valueOf(bundle.getVersion())); if (bundleSymbolicName.equals(bundle.getSymbolicName()) && bundleVersion.equals(curBundleVersion)) { return bundle; } } return null; } catch (IOException ioe) { throw new PortalException(ioe); } } public Bundle getBundle(long bundleId) { if (_framework == null) { return null; } BundleContext bundleContext = _framework.getBundleContext(); return bundleContext.getBundle(bundleId); } @Override public URL getBundleResource(long bundleId, String name) { Bundle bundle = getBundle(bundleId); if (bundle == null) { return null; } return bundle.getResource(name); } @Override public Framework getFramework() { return _framework; } @Override public String getState(long bundleId) throws PortalException { _checkPermission(); Bundle bundle = getBundle(bundleId); if (bundle == null) { throw new PortalException("No bundle with ID " + bundleId); } int state = bundle.getState(); if (state == Bundle.ACTIVE) { return "active"; } else if (state == Bundle.INSTALLED) { return "installed"; } else if (state == Bundle.RESOLVED) { return "resolved"; } else if (state == Bundle.STARTING) { return "starting"; } else if (state == Bundle.STOPPING) { return "stopping"; } else if (state == Bundle.UNINSTALLED) { return "uninstalled"; } else { return StringPool.BLANK; } } @Override public void initFramework() throws Exception { if (_log.isDebugEnabled()) { _log.debug("Initializing the OSGi framework"); } _initRequiredStartupDirs(); List<ServiceLoaderCondition> serviceLoaderConditions = ServiceLoader.load(ServiceLoaderCondition.class); ServiceLoaderCondition serviceLoaderCondition = serviceLoaderConditions.get(0); if (_log.isDebugEnabled()) { Class<?> clazz = serviceLoaderCondition.getClass(); _log.debug( "Using conditional loading to find the OSGi framework " + "factory " + clazz.getName()); } List<FrameworkFactory> frameworkFactories = ServiceLoader.load( FrameworkFactory.class, serviceLoaderCondition); FrameworkFactory frameworkFactory = frameworkFactories.get(0); if (_log.isDebugEnabled()) { Class<?> clazz = frameworkFactory.getClass(); _log.debug("Using the OSGi framework factory " + clazz.getName()); } Map<String, String> properties = _buildFrameworkProperties( frameworkFactory.getClass()); if (_log.isDebugEnabled()) { _log.debug("Creating a new OSGi framework instance"); } _framework = frameworkFactory.newFramework(properties); if (_log.isDebugEnabled()) { _log.debug("Initializing the new OSGi framework instance"); } _framework.init(); if (_log.isDebugEnabled()) { _log.debug("Binding the OSGi framework to the registry API"); } RegistryUtil.setRegistry( new RegistryImpl(_framework.getBundleContext())); if (_log.isDebugEnabled()) { _log.debug( "Binding the OSGi framework to the service tracker map " + "factory"); } ServiceTrackerMapFactoryUtil.setServiceTrackerMapFactory( new ServiceTrackerMapFactoryImpl(_framework.getBundleContext())); if (_log.isDebugEnabled()) { _log.debug("Initialized the OSGi framework"); } } @Override public void registerContext(Object context) { if (context == null) { return; } if (_log.isDebugEnabled()) { _log.debug("Registering context " + context); } if ((context instanceof ApplicationContext) && PropsValues.MODULE_FRAMEWORK_REGISTER_LIFERAY_SERVICES) { ApplicationContext applicationContext = (ApplicationContext)context; _registerApplicationContext(applicationContext); } else if (context instanceof ServletContext) { ServletContext servletContext = (ServletContext)context; _registerServletContext(servletContext); } if (_log.isDebugEnabled()) { _log.debug("Registered context " + context); } } @Override public void setBundleStartLevel(long bundleId, int startLevel) throws PortalException { _checkPermission(); Bundle bundle = getBundle(bundleId); if (bundle == null) { throw new PortalException("No bundle with ID " + bundleId); } BundleStartLevel bundleStartLevel = bundle.adapt( BundleStartLevel.class); bundleStartLevel.setStartLevel(startLevel); } public void startBundle( Bundle bundle, int options, boolean checkPermissions) throws PortalException { if (checkPermissions) { _checkPermission(); } if (_isFragmentBundle(bundle) || ((bundle.getState() & Bundle.ACTIVE) == Bundle.ACTIVE)) { return; } try { bundle.start(options); } catch (BundleException be) { _log.error(be, be); throw new PortalException(be); } } @Override public void startBundle(long bundleId) throws PortalException { startBundle(bundleId, 0); } @Override public void startBundle(long bundleId, int options) throws PortalException { Bundle bundle = getBundle(bundleId); if (bundle == null) { throw new PortalException("No bundle with ID " + bundleId); } startBundle(bundle, 0, true); } @Override public void startFramework() throws Exception { if (_log.isDebugEnabled()) { _log.debug("Starting the OSGi framework"); } _framework.start(); _setUpPrerequisiteFrameworkServices(_framework.getBundleContext()); Set<Bundle> initialBundles = _setUpInitialBundles(); _startDynamicBundles(initialBundles); if (_log.isDebugEnabled()) { _log.debug("Started the OSGi framework"); } } @Override public void startRuntime() throws Exception { if (_framework == null) { return; } if (_log.isDebugEnabled()) { _log.debug("Starting the OSGi runtime"); } FrameworkStartLevel frameworkStartLevel = _framework.adapt( FrameworkStartLevel.class); frameworkStartLevel.setStartLevel( PropsValues.MODULE_FRAMEWORK_RUNTIME_START_LEVEL); if (_log.isDebugEnabled()) { _log.debug("Started the OSGi runtime"); } } @Override public void stopBundle(long bundleId) throws PortalException { stopBundle(bundleId, 0); } @Override public void stopBundle(long bundleId, int options) throws PortalException { _checkPermission(); Bundle bundle = getBundle(bundleId); if (bundle == null) { throw new PortalException("No bundle with ID " + bundleId); } try { bundle.stop(options); } catch (BundleException be) { _log.error(be, be); throw new PortalException(be); } } @Override public void stopFramework(long timeout) throws Exception { if (_framework == null) { return; } Registry registry = RegistryUtil.getRegistry(); if (registry instanceof RegistryImpl) { RegistryImpl registryImpl = (RegistryImpl)registry; registryImpl.closeServiceTrackers(); } ServiceTrackerMapFactory serviceTrackerMapFactory = ServiceTrackerMapFactoryUtil.getServiceTrackerMapFactory(); if (serviceTrackerMapFactory instanceof ServiceTrackerMapFactoryImpl) { ServiceTrackerMapFactoryImpl serviceTrackerMapFactoryImpl = (ServiceTrackerMapFactoryImpl)serviceTrackerMapFactory; serviceTrackerMapFactoryImpl.clearServiceTrackerMaps(); } _framework.stop(); FrameworkEvent frameworkEvent = _framework.waitForStop(timeout); if (frameworkEvent.getType() == FrameworkEvent.WAIT_TIMEDOUT) { _log.error( "OSGi framework event " + frameworkEvent + " triggered after a " + timeout + "ms timeout"); } else if (_log.isInfoEnabled()) { _log.info(frameworkEvent); } RegistryUtil.setRegistry(null); ServiceTrackerMapFactoryUtil.setServiceTrackerMapFactory(null); } @Override public void stopRuntime() throws Exception { if (_framework == null) { return; } FrameworkStartLevel frameworkStartLevel = _framework.adapt( FrameworkStartLevel.class); frameworkStartLevel.setStartLevel( PropsValues.MODULE_FRAMEWORK_BEGINNING_START_LEVEL); } @Override public void uninstallBundle(long bundleId) throws PortalException { _checkPermission(); Bundle bundle = getBundle(bundleId); if (bundle == null) { throw new PortalException("No bundle with ID " + bundleId); } try { bundle.uninstall(); } catch (BundleException be) { _log.error(be, be); throw new PortalException(be); } } @Override public void unregisterContext(Object context) { if (context == null) { return; } if (_log.isDebugEnabled()) { _log.debug("Unregistering context " + context); } if (!(context instanceof ApplicationContext)) { return; } _unregisterApplicationContext((ApplicationContext)context); if (_log.isDebugEnabled()) { _log.debug("Registered context " + context); } } @Override public void updateBundle(long bundleId) throws PortalException { updateBundle(bundleId, null); } @Override public void updateBundle(long bundleId, InputStream inputStream) throws PortalException { _checkPermission(); Bundle bundle = getBundle(bundleId); if (bundle == null) { throw new PortalException("No bundle with ID " + bundleId); } try { bundle.update(inputStream); } catch (BundleException be) { _log.error(be, be); throw new PortalException(be); } } private Bundle _addBundle( String location, InputStream inputStream, boolean checkPermission) throws PortalException { if (_framework == null) { throw new IllegalStateException( "OSGi framework is not initialized"); } if (checkPermission) { _checkPermission(); } BundleContext bundleContext = _framework.getBundleContext(); if (inputStream != null) { UnsyncBufferedInputStream unsyncBufferedInputStream = new UnsyncBufferedInputStream(inputStream); unsyncBufferedInputStream.mark(1024 * 1000); Bundle bundle = null; if (location.startsWith("reference:")) { bundle = _getStaticBundle( bundleContext, unsyncBufferedInputStream, location); } else { bundle = getBundle(bundleContext, unsyncBufferedInputStream); } try { unsyncBufferedInputStream.reset(); } catch (IOException ioe) { throw new PortalException(ioe); } if (bundle != null) { return bundle; } inputStream = unsyncBufferedInputStream; } try { return bundleContext.installBundle(location, inputStream); } catch (BundleException be) { _log.error(be, be); throw new PortalException(be); } } private Map<String, String> _buildFrameworkProperties(Class<?> clazz) { if (_log.isDebugEnabled()) { _log.debug("Building OSGi framework properties"); } Map<String, String> properties = new HashMap<>(); // Release properties.put( Constants.BUNDLE_DESCRIPTION, ReleaseInfo.getReleaseInfo()); properties.put(Constants.BUNDLE_NAME, ReleaseInfo.getName()); properties.put(Constants.BUNDLE_VENDOR, ReleaseInfo.getVendor()); properties.put(Constants.BUNDLE_VERSION, ReleaseInfo.getVersion()); // Fileinstall. See LPS-56385. properties.put( FrameworkPropsKeys.FELIX_FILEINSTALL_DIR, _getFelixFileInstallDir()); properties.put( FrameworkPropsKeys.FELIX_FILEINSTALL_POLL, String.valueOf(PropsValues.MODULE_FRAMEWORK_AUTO_DEPLOY_INTERVAL)); properties.put( FrameworkPropsKeys.FELIX_FILEINSTALL_START_LEVEL, String.valueOf( PropsValues.MODULE_FRAMEWORK_DYNAMIC_INSTALL_START_LEVEL)); properties.put( FrameworkPropsKeys.FELIX_FILEINSTALL_TMPDIR, SystemProperties.get(SystemProperties.TMP_DIR)); // Framework properties.put( Constants.FRAMEWORK_BUNDLE_PARENT, Constants.FRAMEWORK_BUNDLE_PARENT_APP); properties.put( Constants.FRAMEWORK_STORAGE, PropsValues.MODULE_FRAMEWORK_STATE_DIR); properties.put("eclipse.security", null); properties.put("java.security.manager", null); properties.put("org.osgi.framework.security", null); ProtectionDomain protectionDomain = clazz.getProtectionDomain(); CodeSource codeSource = protectionDomain.getCodeSource(); URL codeSourceURL = codeSource.getLocation(); properties.put( FrameworkPropsKeys.OSGI_FRAMEWORK, codeSourceURL.toExternalForm()); File frameworkFile = new File(codeSourceURL.getFile()); properties.put( FrameworkPropsKeys.OSGI_INSTALL_AREA, frameworkFile.getParent()); // Overrides Properties extraProperties = PropsUtil.getProperties( PropsKeys.MODULE_FRAMEWORK_PROPERTIES, true); for (Map.Entry<Object, Object> entry : extraProperties.entrySet()) { String key = (String)entry.getKey(); String value = (String)entry.getValue(); // We need to support an empty string and a null value distinctly. // This is due to some different behaviors between OSGi // implementations. If a property is passed as xyz= it will be // treated as an empty string. Otherwise, xyz=null will be treated // as an explicit null value. if (value.equals(StringPool.NULL)) { value = null; } properties.put(key, value); } String systemPackagesExtra = _getSystemPackagesExtra(); properties.put( Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, systemPackagesExtra); if (_log.isDebugEnabled()) { for (Entry<String, String> entry : properties.entrySet()) { _log.debug( "OSGi framework property key \"" + entry.getKey() + "\" with value \"" + entry.getValue() + "\""); } } return properties; } private void _checkPermission() throws PrincipalException { PermissionChecker permissionChecker = PermissionThreadLocal.getPermissionChecker(); if (permissionChecker == null) { throw new PrincipalException(); } if (!permissionChecker.isOmniadmin()) { throw new PrincipalException.MustBeOmniadmin(permissionChecker); } } private String _getFelixFileInstallDir() { return PropsValues.MODULE_FRAMEWORK_PORTAL_DIR + StringPool.COMMA + StringUtil.merge(PropsValues.MODULE_FRAMEWORK_AUTO_DEPLOY_DIRS); } private Dictionary<String, Object> _getProperties( Object bean, String beanName) { HashMapDictionary<String, Object> properties = new HashMapDictionary<>(); Map<String, Object> osgiBeanProperties = OSGiBeanProperties.Convert.fromObject(bean); if (osgiBeanProperties != null) { properties.putAll(osgiBeanProperties); } properties.put(ServicePropsKeys.BEAN_ID, beanName); properties.put(ServicePropsKeys.ORIGINAL_BEAN, Boolean.TRUE); properties.put(ServicePropsKeys.VENDOR, ReleaseInfo.getVendor()); return properties; } private Bundle _getStaticBundle( BundleContext bundleContext, InputStream inputStream, String location) throws PortalException { try { JarInputStream jarInputStream = new JarInputStream(inputStream); Manifest manifest = jarInputStream.getManifest(); if (manifest == null) { throw new IllegalStateException( "No manifest found at location " + location); } Attributes attributes = manifest.getMainAttributes(); String bundleSymbolicNameAttributeValue = attributes.getValue( Constants.BUNDLE_SYMBOLICNAME); Parameters parameters = OSGiHeader.parseHeader( bundleSymbolicNameAttributeValue); Set<String> set = parameters.keySet(); Iterator<String> iterator = set.iterator(); String bundleSymbolicName = iterator.next(); String bundleVersionAttributeValue = attributes.getValue( Constants.BUNDLE_VERSION); Version bundleVersion = Version.parseVersion( bundleVersionAttributeValue); for (Bundle bundle : bundleContext.getBundles()) { if (bundleSymbolicName.equals(bundle.getSymbolicName())) { Version curBundleVersion = Version.parseVersion( String.valueOf(bundle.getVersion())); if (bundleVersion.equals(curBundleVersion)) { return bundle; } else { bundle.uninstall(); _refreshBundles(Collections.singletonList(bundle)); return null; } } } return null; } catch (Exception e) { throw new PortalException(e); } } private String _getSystemPackagesExtra() { String[] systemPackagesExtra = PropsValues.MODULE_FRAMEWORK_SYSTEM_PACKAGES_EXTRA; StringBundler sb = new StringBundler(); for (String extraPackage : systemPackagesExtra) { sb.append(extraPackage); sb.append(StringPool.COMMA); } Manifest extraPackagesManifest = null; Class<?> clazz = getClass(); InputStream inputStream = clazz.getResourceAsStream( "/META-INF/system.packages.extra.mf"); try { extraPackagesManifest = new Manifest(inputStream); } catch (IOException ioe) { ReflectionUtil.throwException(ioe); } Attributes attributes = extraPackagesManifest.getMainAttributes(); String exportedPackages = attributes.getValue("Export-Package"); sb.append(exportedPackages); if (_log.isDebugEnabled()) { String s = sb.toString(); s = s.replace(",", "\n"); _log.debug( "The portal's system bundle is exporting the following " + "packages:\n" + s); } return sb.toString(); } private boolean _hasLazyActivationPolicy(Bundle bundle) { Dictionary<String, String> headers = bundle.getHeaders(); String fragmentHost = headers.get(Constants.FRAGMENT_HOST); if (fragmentHost != null) { return false; } String activationPolicy = headers.get( Constants.BUNDLE_ACTIVATIONPOLICY); if (activationPolicy == null) { return false; } Parameters parameters = OSGiHeader.parseHeader(activationPolicy); if (parameters.containsKey(Constants.ACTIVATION_LAZY)) { return true; } return false; } private void _initRequiredStartupDirs() { if (_log.isDebugEnabled()) { _log.debug("Initializing required startup directories"); } String[] dirNames = StringUtil.split(_getFelixFileInstallDir()); for (String dirName : dirNames) { FileUtil.mkdirs(dirName); } FileUtil.mkdirs(PropsValues.MODULE_FRAMEWORK_BASE_DIR + "/static"); FileUtil.mkdirs( PropsValues.MODULE_FRAMEWORK_MARKETPLACE_DIR + "/override"); } private Bundle _installInitialBundle( String location, InputStream inputStream) { try { if (_log.isDebugEnabled()) { _log.debug("Adding initial bundle " + location.toString()); } Bundle bundle = _addBundle( "reference:" + location, inputStream, false); if (_log.isDebugEnabled()) { _log.debug("Added initial bundle " + bundle); } if ((bundle == null) || _isFragmentBundle(bundle)) { return bundle; } if (_log.isDebugEnabled()) { _log.debug( "Setting bundle " + bundle + " at start level " + PropsValues.MODULE_FRAMEWORK_BEGINNING_START_LEVEL); } BundleStartLevel bundleStartLevel = bundle.adapt( BundleStartLevel.class); bundleStartLevel.setStartLevel( PropsValues.MODULE_FRAMEWORK_BEGINNING_START_LEVEL); if (_log.isDebugEnabled()) { _log.debug("Starting initial bundle " + bundle); } bundle.start(); if (_log.isDebugEnabled()) { _log.debug("Started bundle " + bundle); } return bundle; } catch (Exception e) { _log.error(e, e); return null; } } private boolean _isFragmentBundle(Bundle bundle) { BundleRevision bundleRevision = bundle.adapt(BundleRevision.class); if ((bundleRevision.getTypes() & BundleRevision.TYPE_FRAGMENT) == 0) { return false; } return true; } private boolean _isIgnoredInterface(String interfaceClassName) { for (String ignoredClass : PropsValues.MODULE_FRAMEWORK_SERVICES_IGNORED_INTERFACES) { if (!ignoredClass.startsWith(StringPool.EXCLAMATION) && (ignoredClass.equals(interfaceClassName) || (ignoredClass.endsWith(StringPool.STAR) && interfaceClassName.startsWith( ignoredClass.substring(0, ignoredClass.length() - 1))))) { return true; } } return false; } private void _refreshBundles(List<Bundle> refreshBundles) { FrameworkWiring frameworkWiring = _framework.adapt( FrameworkWiring.class); final DefaultNoticeableFuture<FrameworkEvent> defaultNoticeableFuture = new DefaultNoticeableFuture<>(); frameworkWiring.refreshBundles( refreshBundles, new FrameworkListener() { @Override public void frameworkEvent(FrameworkEvent frameworkEvent) { defaultNoticeableFuture.set(frameworkEvent); } }); try { FrameworkEvent frameworkEvent = defaultNoticeableFuture.get(); if (frameworkEvent.getType() != FrameworkEvent.PACKAGES_REFRESHED) { throw frameworkEvent.getThrowable(); } } catch (Throwable t) { ReflectionUtil.throwException(t); } } private void _registerApplicationContext( ApplicationContext applicationContext) { if (_log.isDebugEnabled()) { _log.debug("Register application context"); } List<ServiceRegistration<?>> serviceRegistrations = new ArrayList<>(); for (String beanName : applicationContext.getBeanDefinitionNames()) { Object bean = null; try { bean = applicationContext.getBean(beanName); } catch (BeanIsAbstractException biae) { } catch (Exception e) { _log.error(e, e); } if (bean != null) { ServiceRegistration<?> serviceRegistration = _registerService( _framework.getBundleContext(), beanName, bean); if (serviceRegistration != null) { serviceRegistrations.add(serviceRegistration); } } } _springContextServices.put(applicationContext, serviceRegistrations); } private ServiceRegistration<?> _registerService( BundleContext bundleContext, String beanName, Object bean) { Set<Class<?>> interfaces = OSGiBeanProperties.Service.interfaces(bean); interfaces.add(bean.getClass()); List<String> names = new ArrayList<>(interfaces.size()); for (Class<?> interfaceClass : interfaces) { String interfaceClassName = interfaceClass.getName(); if (!_isIgnoredInterface(interfaceClassName)) { names.add(interfaceClassName); } } if (names.isEmpty()) { return null; } ServiceRegistration<?> serviceRegistration = bundleContext.registerService( names.toArray(new String[names.size()]), bean, _getProperties(bean, beanName)); if (_log.isDebugEnabled()) { _log.debug( "Registered service as " + serviceRegistration.getReference()); } return serviceRegistration; } private void _registerServletContext(ServletContext servletContext) { BundleContext bundleContext = _framework.getBundleContext(); if (_log.isDebugEnabled()) { _log.debug("Register servlet context"); } ServiceRegistration<?> serviceRegistration = bundleContext.registerService( new String[] {ServletContext.class.getName()}, servletContext, _getProperties(servletContext, "liferayServletContext")); if (_log.isDebugEnabled()) { _log.debug( "Registered servlet context as " + serviceRegistration.getReference()); } } private Set<Bundle> _setUpInitialBundles() throws Exception { if (_log.isDebugEnabled()) { _log.debug("Starting initial bundles"); } BundleContext bundleContext = _framework.getBundleContext(); ThrowableCollector throwableCollector = new ThrowableCollector(); Dictionary<String, Object> dictionary = new HashMapDictionary<>(); dictionary.put("throwable.collector", "initial.bundles"); bundleContext.registerService( ThrowableCollector.class, throwableCollector, dictionary); final List<Bundle> bundles = new ArrayList<>(); final List<Path> jarPaths = new ArrayList<>(); Files.walkFileTree( Paths.get(PropsValues.MODULE_FRAMEWORK_BASE_DIR, "static"), new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile( Path filePath, BasicFileAttributes basicFileAttributes) throws IOException { Path fileNamePath = filePath.getFileName(); String fileName = StringUtil.toLowerCase( fileNamePath.toString()); if (!fileName.endsWith(".jar")) { return FileVisitResult.CONTINUE; } Matcher matcher = _pattern.matcher(fileName); if (!matcher.matches()) { jarPaths.add(filePath.toAbsolutePath()); return FileVisitResult.CONTINUE; } if (_log.isWarnEnabled()) { _log.warn( "Override static jar " + fileName + " has an invalid name and will be ignored"); } return FileVisitResult.CONTINUE; } }); Collections.sort(jarPaths); String prefix = "reference:".concat(_STATIC_JAR); List<Bundle> refreshBundles = new ArrayList<>(); for (Bundle bundle : bundleContext.getBundles()) { String location = bundle.getLocation(); if (!location.startsWith(prefix)) { continue; } Path filePath = Paths.get(location.substring(prefix.length())); if (jarPaths.contains(filePath)) { bundles.add(bundle); continue; } bundle.uninstall(); refreshBundles.add(bundle); if (_log.isInfoEnabled()) { _log.info( "Uninstalled orphan overriding static JAR bundle " + location); } } _refreshBundles(refreshBundles); refreshBundles.clear(); for (String staticJarFileName : PropsValues.MODULE_FRAMEWORK_STATIC_JARS) { File staticJarFile = new File( PropsValues.LIFERAY_LIB_PORTAL_DIR, staticJarFileName); if (staticJarFile.exists()) { jarPaths.add(staticJarFile.toPath()); } else { _log.error("Missing " + staticJarFile); } } Set<String> overrideStaticFileNames = new HashSet<>(); for (Path jarPath : jarPaths) { try (InputStream inputStream = Files.newInputStream(jarPath)) { String path = jarPath.toString(); Bundle bundle = _installInitialBundle( _STATIC_JAR.concat(path), inputStream); if (bundle != null) { bundles.add(bundle); overrideStaticFileNames.add( path.substring(path.lastIndexOf(StringPool.SLASH) + 1)); } } } String deployDir = bundleContext.getProperty("lpkg.deployer.dir"); File file = new File( deployDir + StringPool.SLASH + StaticLPKGResolver.getStaticLPKGFileName()); if (file.exists()) { try (ZipFile zipFile = new ZipFile(file)) { Enumeration<? extends ZipEntry> enumeration = zipFile.entries(); List<ZipEntry> zipEntries = new ArrayList<>(); while (enumeration.hasMoreElements()) { ZipEntry zipEntry = enumeration.nextElement(); String name = StringUtil.toLowerCase(zipEntry.getName()); if (!name.endsWith(".jar")) { continue; } zipEntries.add(zipEntry); } Collections.sort( zipEntries, new Comparator<ZipEntry>() { @Override public int compare( ZipEntry zipEntry1, ZipEntry zipEntry2) { String name1 = zipEntry1.getName(); String name2 = zipEntry2.getName(); return name1.compareTo(name2); } }); for (ZipEntry zipEntry : zipEntries) { try (InputStream inputStream = zipFile.getInputStream( zipEntry)) { String zipEntryName = zipEntry.getName(); Matcher matcher = _pattern.matcher(zipEntryName); if (matcher.matches()) { String fileName = matcher.group(1) + matcher.group(4); if (overrideStaticFileNames.contains(fileName)) { if (_log.isInfoEnabled()) { StringBundler sb = new StringBundler(7); sb.append(zipFile); sb.append(":"); sb.append(zipEntry); sb.append(" is overridden by "); sb.append( PropsValues.MODULE_FRAMEWORK_BASE_DIR); sb.append("/static/"); sb.append(fileName); _log.info(sb.toString()); } continue; } } Bundle bundle = _installInitialBundle( StringPool.SLASH.concat(zipEntryName), inputStream); if (bundle != null) { bundles.add(bundle); } } } } } Set<String> overrideLPKGFileNames = new HashSet<>(); try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream( Paths.get(deployDir, "override"))) { for (Path path : directoryStream) { String fileName = String.valueOf(path.getFileName()); String pathName = StringUtil.toLowerCase(fileName); if (pathName.endsWith("jar")) { overrideLPKGFileNames.add(fileName); } } } for (Bundle bundle : bundleContext.getBundles()) { String location = bundle.getLocation(); Matcher matcher = _pattern.matcher(location); if (matcher.matches()) { location = matcher.group(1) + matcher.group(4); } if (overrideLPKGFileNames.contains(location)) { bundle.uninstall(); } } Bundle[] initialBundles = bundleContext.getBundles(); FrameworkStartLevel frameworkStartLevel = _framework.adapt( FrameworkStartLevel.class); frameworkStartLevel.setStartLevel( PropsValues.MODULE_FRAMEWORK_BEGINNING_START_LEVEL); for (final Bundle bundle : bundles) { if (_isFragmentBundle(bundle)) { continue; } final CountDownLatch countDownLatch = new CountDownLatch(1); BundleTracker<Void> bundleTracker = new BundleTracker<Void>( _framework.getBundleContext(), Bundle.ACTIVE, null) { @Override public Void addingBundle( Bundle trackedBundle, BundleEvent bundleEvent) { if (trackedBundle == bundle) { countDownLatch.countDown(); close(); } return null; } }; bundleTracker.open(); countDownLatch.await(); } throwableCollector.rethrow(); if (_log.isDebugEnabled()) { _log.debug("Started initial bundles"); } Bundle[] installedBundles = bundleContext.getBundles(); List<String> hostBundleSymbolicNames = new ArrayList<>(); for (Bundle bundle : installedBundles) { Dictionary<String, String> headers = bundle.getHeaders(); String fragmentHost = headers.get(Constants.FRAGMENT_HOST); if (fragmentHost == null) { continue; } int index = fragmentHost.indexOf(CharPool.SEMICOLON); if (index != -1) { fragmentHost = fragmentHost.substring(0, index); } hostBundleSymbolicNames.add(fragmentHost); } for (Bundle bundle : installedBundles) { if (hostBundleSymbolicNames.contains(bundle.getSymbolicName())) { refreshBundles.add(bundle); } } _refreshBundles(refreshBundles); return new HashSet<>(Arrays.asList(initialBundles)); } private void _setUpPrerequisiteFrameworkServices( BundleContext bundleContext) { if (_log.isDebugEnabled()) { _log.debug("Setting up required services"); } Props props = PropsUtil.getProps(); ServiceRegistration<Props> serviceRegistration = bundleContext.registerService( Props.class, props, _getProperties(props, Props.class.getName())); if (_log.isDebugEnabled()) { _log.debug( "Registered required service as " + serviceRegistration.getReference()); } if (_log.isDebugEnabled()) { _log.debug("Registered required services"); } } private void _startDynamicBundles(Set<Bundle> installedBundles) throws Exception { FrameworkStartLevel frameworkStartLevel = _framework.adapt( FrameworkStartLevel.class); final DefaultNoticeableFuture<FrameworkEvent> defaultNoticeableFuture = new DefaultNoticeableFuture<>(); frameworkStartLevel.setStartLevel( PropsValues.MODULE_FRAMEWORK_DYNAMIC_INSTALL_START_LEVEL, new FrameworkListener() { @Override public void frameworkEvent(FrameworkEvent fe) { defaultNoticeableFuture.set(fe); } }); FrameworkEvent frameworkEvent = defaultNoticeableFuture.get(); if (frameworkEvent.getType() == FrameworkEvent.ERROR) { ReflectionUtil.throwException(frameworkEvent.getThrowable()); } BundleContext bundleContext = _framework.getBundleContext(); for (Bundle bundle : bundleContext.getBundles()) { if (installedBundles.contains(bundle) || ((bundle.getState() != Bundle.INSTALLED) && (bundle.getState() != Bundle.RESOLVED))) { continue; } BundleRevision bundleRevision = bundle.adapt(BundleRevision.class); if ((bundleRevision.getTypes() & BundleRevision.TYPE_FRAGMENT) != 0) { continue; } BundleStartLevel bundleStartLevel = bundle.adapt( BundleStartLevel.class); if (bundleStartLevel.getStartLevel() == PropsValues.MODULE_FRAMEWORK_DYNAMIC_INSTALL_START_LEVEL) { try { bundle.start(); } catch (BundleException be) { _log.error( "Unable to start bundle " + bundle.getSymbolicName(), be); } } } } private void _unregisterApplicationContext( ApplicationContext applicationContext) { List<ServiceRegistration<?>> serviceRegistrations = _springContextServices.remove(applicationContext); if (serviceRegistrations == null) { return; } for (ServiceRegistration<?> serviceRegistration : serviceRegistrations) { try { serviceRegistration.unregister(); } catch (IllegalStateException ise) { if (_log.isDebugEnabled()) { _log.debug( "Service registration " + serviceRegistration + " is already unregistered"); } } } } private static final String _STATIC_JAR = "Static-Jar::"; private static final Log _log = LogFactoryUtil.getLog( ModuleFrameworkImpl.class); private static final Pattern _pattern = Pattern.compile( "/?(.*?)(-\\d+\\.\\d+\\.\\d+)(\\..+)?(\\.jar)"); private Framework _framework; private final Map<ApplicationContext, List<ServiceRegistration<?>>> _springContextServices = new ConcurrentHashMap<>(); }