/**
* 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.spring.extender.internal.context;
import com.liferay.osgi.felix.util.AbstractExtender;
import com.liferay.portal.kernel.dao.db.DB;
import com.liferay.portal.kernel.dao.db.DBContext;
import com.liferay.portal.kernel.dao.db.DBManager;
import com.liferay.portal.kernel.dao.db.DBProcessContext;
import com.liferay.portal.kernel.model.Release;
import com.liferay.portal.kernel.module.framework.ModuleServiceLifecycle;
import com.liferay.portal.kernel.service.configuration.configurator.ServiceConfigurator;
import com.liferay.portal.kernel.upgrade.UpgradeException;
import com.liferay.portal.kernel.upgrade.UpgradeStep;
import com.liferay.portal.kernel.util.GetterUtil;
import com.liferay.portal.kernel.util.InfrastructureUtil;
import com.liferay.portal.kernel.util.StringUtil;
import com.liferay.portal.kernel.util.Validator;
import com.liferay.portal.kernel.xml.SAXReaderUtil;
import com.liferay.portal.spring.extender.internal.classloader.BundleResolverClassLoader;
import com.liferay.portal.upgrade.registry.UpgradeStepRegistratorTracker;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.List;
import javax.sql.DataSource;
import org.apache.felix.dm.DependencyManager;
import org.apache.felix.dm.ServiceDependency;
import org.apache.felix.utils.extender.Extension;
import org.apache.felix.utils.log.Logger;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
/**
* @author Miguel Pastor
*/
@Component(immediate = true)
public class ModuleApplicationContextExtender extends AbstractExtender {
@Activate
protected void activate(BundleContext bundleContext) throws Exception {
_dependencyManager = new DependencyManager(bundleContext);
_logger = new Logger(bundleContext);
start(bundleContext);
}
@Deactivate
protected void deactivate(BundleContext bundleContext) throws Exception {
stop(bundleContext);
}
@Override
protected void debug(Bundle bundle, String s) {
_logger.log(Logger.LOG_DEBUG, "[" + bundle + "] " + s);
}
@Override
protected Extension doCreateExtension(Bundle bundle) throws Exception {
Dictionary<String, String> headers = bundle.getHeaders();
if (headers.get("Liferay-Spring-Context") == null) {
return null;
}
return new ModuleApplicationContextExtension(bundle);
}
@Override
protected void error(String s, Throwable throwable) {
_logger.log(Logger.LOG_ERROR, s, throwable);
}
@Reference(
target = "(&(bean.id=liferayDataSource)(original.bean=true))",
unbind = "-"
)
protected void setDataSource(DataSource dataSource) {
}
@Reference(target = "(original.bean=true)", unbind = "-")
protected void setInfrastructureUtil(
InfrastructureUtil infrastructureUtil) {
}
@Reference(target = ModuleServiceLifecycle.PORTAL_INITIALIZED, unbind = "-")
protected void setModuleServiceLifecycle(
ModuleServiceLifecycle moduleServiceLifecycle) {
}
@Reference(target = "(original.bean=true)", unbind = "-")
protected void setSaxReaderUtil(SAXReaderUtil saxReaderUtil) {
}
@Reference(target = "(original.bean=true)", unbind = "-")
protected void setServiceConfigurator(
ServiceConfigurator serviceConfigurator) {
_serviceConfigurator = serviceConfigurator;
}
@Override
protected void warn(Bundle bundle, String s, Throwable throwable) {
_logger.log(Logger.LOG_DEBUG, "[" + bundle + "] " + s);
}
private DependencyManager _dependencyManager;
private Logger _logger;
private ServiceConfigurator _serviceConfigurator;
private class ModuleApplicationContextExtension implements Extension {
public ModuleApplicationContextExtension(Bundle bundle) {
_bundle = bundle;
}
@Override
public void destroy() throws Exception {
for (ServiceRegistration<UpgradeStep>
upgradeStepServiceRegistration :
_upgradeStepServiceRegistrations) {
upgradeStepServiceRegistration.unregister();
}
if (_component != null) {
_dependencyManager.remove(_component);
}
}
public String getSQLTemplateString(String templateName)
throws UpgradeException {
URL resource = _bundle.getResource("/META-INF/sql/" + templateName);
if (resource == null) {
_logger.log(
Logger.LOG_DEBUG,
"Unable to locate SQL template " + templateName);
return null;
}
try (InputStream inputStream = resource.openStream()) {
return StringUtil.read(inputStream);
}
catch (IOException ioe) {
throw new UpgradeException(
"Unable to read SQL template " + templateName, ioe);
}
}
@Override
public void start() throws Exception {
_component = _dependencyManager.createComponent();
BundleContext bundleContext =
ModuleApplicationContextExtender.this.getBundleContext();
Bundle bundle = bundleContext.getBundle();
_component.setImplementation(
new ModuleApplicationContextRegistrator(
_bundle, bundle, _serviceConfigurator));
ClassLoader classLoader = new BundleResolverClassLoader(
_bundle, bundle);
List<ContextDependency> contextDependencies =
_processServiceReferences(_bundle);
for (ContextDependency contextDependency : contextDependencies) {
ServiceDependency serviceDependency =
_dependencyManager.createServiceDependency();
serviceDependency.setRequired(true);
Class<?> serviceClass = Class.forName(
contextDependency.getServiceClassName(), false,
classLoader);
serviceDependency.setService(
serviceClass, contextDependency.getFilterString());
_component.add(serviceDependency);
}
Dictionary<String, String> headers = _bundle.getHeaders();
String requireSchemaVersion = headers.get(
"Liferay-Require-SchemaVersion");
if (Validator.isNull(requireSchemaVersion)) {
_generateReleaseInfo();
}
_dependencyManager.add(_component);
_upgradeStepServiceRegistrations = _processInitialUpgrade();
}
private void _generateReleaseInfo() {
ServiceDependency serviceDependency =
_dependencyManager.createServiceDependency();
serviceDependency.setRequired(true);
serviceDependency.setService(
Release.class,
"(&(release.bundle.symbolic.name=" + _bundle.getSymbolicName() +
")(release.schema.version=" + _bundle.getVersion() + "))");
_component.add(serviceDependency);
}
private List<ServiceRegistration<UpgradeStep>>
_processInitialUpgrade() {
Dictionary<String, String> headers = _bundle.getHeaders();
String upgradeToSchemaVersion = GetterUtil.getString(
headers.get("Liferay-Require-SchemaVersion"),
headers.get("Bundle-Version"));
Dictionary<String, Object> properties = new Hashtable<>();
properties.put("upgrade.initial.database.creation", "true");
return UpgradeStepRegistratorTracker.register(
ModuleApplicationContextExtender.this.getBundleContext(),
_bundle.getSymbolicName(), "0.0.0", upgradeToSchemaVersion,
properties,
new UpgradeStep() {
@Override
public String toString() {
return "Initial Database Creation";
}
@Override
public void upgrade(DBProcessContext dbProcessContext)
throws UpgradeException {
DBContext dbContext = dbProcessContext.getDBContext();
DBManager dbManager = dbContext.getDBManager();
DB db = dbManager.getDB();
String tablesSQL = getSQLTemplateString("tables.sql");
String sequencesSQL = getSQLTemplateString(
"sequences.sql");
String indexesSQL = getSQLTemplateString("indexes.sql");
try {
if (tablesSQL != null) {
db.runSQLTemplateString(tablesSQL, true, true);
}
if (sequencesSQL != null) {
db.runSQLTemplateString(
sequencesSQL, true, true);
}
if (indexesSQL != null) {
db.runSQLTemplateString(indexesSQL, true, true);
}
}
catch (Exception e) {
throw new UpgradeException(e);
}
}
});
}
private List<ContextDependency> _processServiceReferences(Bundle bundle)
throws IOException {
List<ContextDependency> contextDependencies = new ArrayList<>();
URL url = bundle.getEntry("OSGI-INF/context/context.dependencies");
if (url == null) {
return contextDependencies;
}
List<String> lines = new ArrayList<>();
StringUtil.readLines(url.openStream(), lines);
for (String line : lines) {
if (Validator.isNull(line)) {
continue;
}
line = line.trim();
String[] array = line.split(" ");
String filterString = "";
if (array.length > 1) {
filterString = array[1];
}
contextDependencies.add(
new ContextDependency(array[0], filterString));
}
return contextDependencies;
}
private final Bundle _bundle;
private org.apache.felix.dm.Component _component;
private List<ServiceRegistration<UpgradeStep>>
_upgradeStepServiceRegistrations;
private class ContextDependency {
public ContextDependency(
String serviceClassName, String filterString) {
this.serviceClassName = serviceClassName;
if (!filterString.equals("")) {
this.filterString = filterString;
}
}
public String getFilterString() {
return filterString;
}
public String getServiceClassName() {
return serviceClassName;
}
protected String filterString;
protected final String serviceClassName;
}
}
}