package com.dotmarketing.util;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.servlet.ServletContextEvent;
import com.dotcms.repackage.org.apache.commons.io.IOUtils;
import com.dotcms.repackage.org.apache.felix.framework.FrameworkFactory;
import com.dotcms.repackage.org.apache.felix.framework.util.FelixConstants;
import com.dotcms.repackage.org.apache.felix.http.proxy.DispatcherTracker;
import com.dotcms.repackage.org.apache.felix.main.AutoProcessor;
import com.dotcms.repackage.org.apache.felix.main.Main;
import org.apache.velocity.tools.view.PrimitiveToolboxManager;
import com.dotcms.repackage.org.osgi.framework.BundleActivator;
import com.dotcms.repackage.org.osgi.framework.BundleContext;
import com.dotcms.repackage.org.osgi.framework.ServiceReference;
import com.dotcms.repackage.org.osgi.framework.launch.Framework;
import com.dotmarketing.osgi.HostActivator;
import com.dotmarketing.osgi.OSGIProxyServlet;
import com.dotmarketing.portlets.workflows.business.WorkflowAPIOsgiService;
/**
* Created by Jonathan Gamba
* Date: 9/17/12
*/
public class OSGIUtil {
private static final String FELIX_FILEINSTALL_DIR = "felix.fileinstall.dir";
public static final String BUNDLE_HTTP_BRIDGE_SYMBOLIC_NAME = "org.apache.felix.http.bundle";
private static final String PROPERTY_OSGI_PACKAGES_EXTRA = "org.osgi.framework.system.packages.extra";
public String FELIX_EXTRA_PACKAGES_FILE;
public String FELIX_EXTRA_PACKAGES_FILE_GENERATED;
private static OSGIUtil instance;
public static OSGIUtil getInstance () {
if ( instance == null ) {
instance = new OSGIUtil();
}
return instance;
}
private OSGIUtil () {
}
private static Framework m_fwk;
private ServletContextEvent servletContextEvent;
public Framework initializeFramework () {
if ( servletContextEvent != null ) {
return initializeFramework( servletContextEvent );
}
throw new IllegalArgumentException( "In order to initialize the OSGI framework a ServletContextEvent must be set." );
}
public Framework initializeFramework ( ServletContextEvent context ) {
if(!Config.getBooleanProperty("felix.osgi.enable", true)){
return null;
}
servletContextEvent = context;
String felixDirectory = context.getServletContext().getRealPath( File.separator + "WEB-INF" + File.separator + "felix" );
FELIX_EXTRA_PACKAGES_FILE = felixDirectory + File.separator + "osgi-extra.conf";
FELIX_EXTRA_PACKAGES_FILE_GENERATED = felixDirectory + File.separator + "osgi-extra-generated.conf";
Logger.info( this, "Felix dir: " + felixDirectory );
String bundleDir = felixDirectory + File.separator + "bundle";
String cacheDir = felixDirectory + File.separator + "felix-cache";
String autoLoadDir = felixDirectory + File.separator + "load";
Properties configProps;
String extraPackages;
try {
configProps = loadConfig();
extraPackages = getExtraOSGIPackages();
} catch ( IOException e ) {
Logger.error( this, "Error loading the OSGI framework properties: " + e );
throw new RuntimeException( e );
}
//Setting the OSGI extra packages property
configProps.setProperty( PROPERTY_OSGI_PACKAGES_EXTRA, extraPackages );
// we need gosh to not expecting stdin to work
configProps.setProperty( "gosh.args", "--noi" );
// (2) Load system properties.
Main.loadSystemProperties();
// (4) Copy framework properties from the system properties.
Main.copySystemProperties( propertiesToMap( configProps ) );
// (5) Use the specified auto-deploy directory over default.
if ( bundleDir != null ) {
configProps.setProperty( AutoProcessor.AUTO_DEPLOY_DIR_PROPERY, bundleDir );
}
// (6) Use the specified bundle cache directory over default.
if ( cacheDir != null ) {
configProps.setProperty( com.dotcms.repackage.org.osgi.framework.Constants.FRAMEWORK_STORAGE, cacheDir );
}
// Create host activator;
List<BundleActivator> list = new ArrayList<BundleActivator>();
HostActivator hostActivator = HostActivator.instance();
hostActivator.setServletContext( context.getServletContext() );
list.add( hostActivator );
configProps.put( FelixConstants.SYSTEMBUNDLE_ACTIVATORS_PROP, list );
String felixFileInstallDirPath = configProps.getProperty(FELIX_FILEINSTALL_DIR);
if (felixFileInstallDirPath == null || !new File(felixFileInstallDirPath).isDirectory()) {
configProps.put(FELIX_FILEINSTALL_DIR, autoLoadDir);
}
try {
// (8) Create an instance and initialize the framework.
FrameworkFactory factory = getFrameworkFactory();
m_fwk = factory.newFramework( configProps );
m_fwk.init();
// (9) Use the system bundle context to process the auto-deploy
// and auto-install/auto-start properties.
AutoProcessor.process( configProps, m_fwk.getBundleContext() );
// (10) Start the framework.
m_fwk.start();
Logger.info( this, "osgi felix framework started" );
} catch ( Exception ex ) {
Logger.error( this, "Could not create framework: " + ex );
throw new RuntimeException( ex );
}
return m_fwk;
}
public void stopFramework () {
try {
BundleContext bundleContext = HostActivator.instance().getBundleContext();
//Closing tracker associated to the HttpServlet
DispatcherTracker tracker = OSGIProxyServlet.tracker;
if ( tracker != null ) {
tracker.close();
OSGIProxyServlet.tracker = null;
}
//Unregistering ToolBox services
ServiceReference toolBoxService = getBundleContext().getServiceReference( PrimitiveToolboxManager.class.getName() );
if ( toolBoxService != null ) {
bundleContext.ungetService( toolBoxService );
}
//Unregistering Workflow services
ServiceReference workflowService = getBundleContext().getServiceReference( WorkflowAPIOsgiService.class.getName() );
if ( workflowService != null ) {
bundleContext.ungetService( workflowService );
}
// Stop felix
m_fwk.stop();
// (11) Wait for framework to stop to exit the VM.
m_fwk.waitForStop( 0 );
} catch ( Exception e ) {
Logger.warn( this, "exception while stopping felix!", e );
}
}
public BundleContext getBundleContext () {
return m_fwk.getBundleContext();
}
private static FrameworkFactory getFrameworkFactory () throws Exception {
URL url = Main.class.getClassLoader().getResource( "META-INF/services/org.osgi.framework.launch.FrameworkFactory" );
if ( url != null ) {
BufferedReader br = new BufferedReader( new InputStreamReader( url.openStream() ) );
try {
for ( String s = br.readLine(); s != null; s = br.readLine() ) {
s = s.trim();
// Try to load first non-empty, non-commented line.
if ( (s.length() > 0) && (s.charAt( 0 ) != '#') ) {
Logger.info( OSGIUtil.class, "Loading Factory " + s );
return (FrameworkFactory) Class.forName( s ).newInstance();
}
}
} finally {
if ( br != null ) br.close();
}
}
throw new Exception( "Could not find framework factory." );
}
/**
* Loads all the OSGI configured properties
*
* @return
*/
private Properties loadConfig () {
Properties properties = new Properties();
Iterator<String> it = Config.getKeys();
while ( it.hasNext() ) {
String key = it.next();
if ( key == null ) continue;
if ( key.startsWith( "felix." ) ) {
properties.put( key.substring( 6 ), Config.getStringProperty( key ) );
Logger.info( this, "Loading property " + key.substring( 6 ) + "=" + Config.getStringProperty( key ) );
}
}
return properties;
}
/**
* Returns the packages inside the <strong>osgi-extra.conf</strong> file, those packages are the value
* for the OSGI configuration property <strong>org.osgi.framework.system.packages.extra</strong>.
* <br/><br/>
* The property <strong>org.osgi.framework.system.packages.extra</strong> is use to set the list of packages the
* dotCMS context in going to expose to the OSGI context.
*
* @return
* @throws IOException
*/
public String getExtraOSGIPackages () throws IOException {
String extraPackages;
File f = new File(FELIX_EXTRA_PACKAGES_FILE);
if(!f.exists()){
StringBuilder bob = new StringBuilder();
final Collection<String> list = ResourceCollectorUtil.getResources();
for ( final String name : list ) {
if(name.startsWith("/")) continue;
if(name.contains(":")) continue;
if ( File.separator.equals( "/" ) ) {
bob.append( name.replace( File.separator, "." ) + "," + "\n" );
} else {
//Zip entries have '/' as separator on all platforms
bob.append( (name.replace( File.separator, "." ).replace( "/", "." )) + "," + "\n" );
}
}
bob.append( "com.dotcms.repackage.org.osgi.framework," +
"com.dotcms.repackage.org.osgi.framework.wiring," +
"com.dotcms.repackage.org.osgi.service.packageadmin," +
"com.dotcms.repackage.org.osgi.framework.startlevel," +
"com.dotcms.repackage.org.osgi.service.startlevel," +
"com.dotcms.repackage.org.osgi.service.url," +
"com.dotcms.repackage.org.osgi.util.tracker," +
"com.dotcms.repackage.org.osgi.service.http," +
"javax.inject.Qualifier," +
"javax.servlet.resources," +
"javax.servlet;javax.servlet.http;version=2.5" );
BufferedWriter writer = null;
try {
writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream( FELIX_EXTRA_PACKAGES_FILE_GENERATED ), "utf-8"));
writer.write(bob.toString());
} catch (IOException ex) {
Logger.error(this, ex.getMessage(), ex);
} finally {
try {writer.close();} catch (Exception ex) {Logger.error(this, ex.getMessage(), ex);}
}
}
//Reading the file with the extra packages
FileInputStream inputStream = null;
if(f.exists()){
inputStream = new FileInputStream( FELIX_EXTRA_PACKAGES_FILE );
}else{
inputStream = new FileInputStream( FELIX_EXTRA_PACKAGES_FILE_GENERATED );
}
try {
extraPackages = IOUtils.toString( inputStream );
} finally {
inputStream.close();
}
//Clean up the properties, it is better to keep it simple and in a standard format
extraPackages = extraPackages.replaceAll( "\\\n", "" );
extraPackages = extraPackages.replaceAll( "\\\r", "" );
extraPackages = extraPackages.replaceAll( "\\\\", "" );
return extraPackages;
}
/**
* Transform a given Properties object into a Map
*
* @param props
* @return
*/
private Map<String, String> propertiesToMap ( Properties props ) {
HashMap<String, String> propertiesMap = new HashMap<String, String>();
Enumeration<Object> e = props.keys();
while ( e.hasMoreElements() ) {
String s = (String) e.nextElement();
propertiesMap.put( s, props.getProperty( s ) );
}
return propertiesMap;
}
}