/*
* Copyright (c) 2007-2009, Osmorc Development Team
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright notice, this list
* of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
* * Neither the name of 'Osmorc Development Team' nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.osmorc.run;
import com.intellij.execution.CantRunException;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.Executor;
import com.intellij.execution.configurations.JavaCommandLineState;
import com.intellij.execution.configurations.JavaParameters;
import com.intellij.execution.configurations.ParametersList;
import com.intellij.execution.filters.TextConsoleBuilderImpl;
import com.intellij.execution.process.OSProcessHandler;
import com.intellij.execution.process.ProcessAdapter;
import com.intellij.execution.process.ProcessEvent;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.openapi.compiler.DummyCompileContext;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.JavaSdk;
import com.intellij.openapi.projectRoots.JdkUtil;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.PathsList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.osmorc.facet.OsmorcFacet;
import org.osmorc.frameworkintegration.*;
import org.osmorc.make.BundleCompiler;
import org.osmorc.run.ui.SelectedBundle;
import javax.swing.*;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
/**
* RunState for launching the OSGI framework.
*
* @author <a href="mailto:janthomae@janthomae.de">Jan Thomä</a>
* @author Robert F. Beeger (robert@beeger.net)
* @version $Id$
*/
public class OsgiRunState extends JavaCommandLineState {
private final OsgiRunConfiguration runConfiguration;
private final Project project;
private final Sdk jdkForRun;
private SelectedBundle[] _selectedBundles;
private final FrameworkRunner runner;
private static final String FILE_URL_PREFIX = "file:///";
public OsgiRunState(@NotNull Executor executor,
@NotNull ExecutionEnvironment env,
OsgiRunConfiguration configuration,
Project project,
Sdk projectJdk) {
super(env);
this.runConfiguration = configuration;
this.project = project;
if (configuration.isUseAlternativeJre()) {
String path = configuration.getAlternativeJrePath();
if (path == null || "".equals(path) || !JdkUtil.checkForJre(path)) {
this.jdkForRun = null;
}
else {
this.jdkForRun = JavaSdk.getInstance().createJdk("", configuration.getAlternativeJrePath());
}
}
else {
this.jdkForRun = projectJdk;
}
setConsoleBuilder(new TextConsoleBuilderImpl(project));
FrameworkInstanceDefinition definition = runConfiguration.getInstanceToUse();
FrameworkIntegratorRegistry registry = ServiceManager.getService(project, FrameworkIntegratorRegistry.class);
FrameworkIntegrator integrator = registry.findIntegratorByInstanceDefinition(definition);
runner = integrator.createFrameworkRunner();
runner.init(project, runConfiguration, getRunnerSettings());
}
public boolean requiresRemoteDebugger() {
return runner instanceof ExternalVMFrameworkRunner;
}
protected JavaParameters createJavaParameters() throws ExecutionException {
if (jdkForRun == null) {
throw CantRunException.noJdkConfigured();
}
final JavaParameters params = new JavaParameters();
params.setWorkingDirectory(runner.getWorkingDir());
// only add JDK classes to the classpath
// the rest is is to be provided by bundles
params.configureByProject(project, JavaParameters.JDK_ONLY, jdkForRun);
PathsList classpath = params.getClassPath();
for (VirtualFile libraryFile : runner.getFrameworkStarterLibraries()) {
classpath.add(libraryFile);
}
if (runConfiguration.isIncludeAllBundlesInClassPath()) {
SelectedBundle[] bundles = getSelectedBundles();
for (SelectedBundle bundle : bundles) {
String bundlePath = bundle.getBundleUrl();
bundlePath = bundlePath.substring(FILE_URL_PREFIX.length());
if (bundlePath.indexOf(':') < 0 && bundlePath.charAt(0) != '/') {
bundlePath = "/" + bundlePath;
}
bundlePath = bundlePath.replace('/', File.separatorChar);
classpath.add(bundlePath);
}
}
// setup the main class
params.setMainClass(runner.getMainClass());
// get the bundles to be run.
SelectedBundle[] bundles = getSelectedBundles();
if (bundles == null) {
throw new CantRunException("One or more modules seem to be missing their OSGi facets or you have modules in your run configuration that no longer exist. Please re-add the OSGi facets or clean the run configuration and try again.");
}
// setup the commandline parameters
final ParametersList programParameters = params.getProgramParametersList();
runner.fillCommandLineParameters(programParameters, bundles);
// and the vm parameters
final ParametersList vmParameters = params.getVMParametersList();
runner.fillVmParameters(vmParameters, bundles);
return params;
}
/**
* Here we got the magic. All libs are turned into bundles sorted and returned.
*
* @return the sorted list of all bundles to start.
*/
@Nullable
private SelectedBundle[] getSelectedBundles() {
if (_selectedBundles == null) {
ProgressManager.getInstance().run(new Task.Modal(project, "Preparing bundles...", false) {
public void run(@NotNull ProgressIndicator progressIndicator) {
progressIndicator.setIndeterminate(false);
HashSet<SelectedBundle> selectedBundles = new HashSet<SelectedBundle>();
// the bundles are module names, by now we try to find jar files in the output directory which we can then install
ModuleManager moduleManager = ModuleManager.getInstance(project);
int bundleCount = runConfiguration.getBundlesToDeploy().size();
for (int i = 0; i < bundleCount; i++) {
final SelectedBundle selectedBundle = runConfiguration.getBundlesToDeploy().get(i);
progressIndicator.setFraction(i / bundleCount);
if (selectedBundle.isModule()) {
// use the output jar name if it is a module
try {
final Module module = moduleManager.findModuleByName(selectedBundle.getName());
if (!OsmorcFacet.hasOsmorcFacet(module)) {
// actually this should not happen, but it seemed to happen once, so we check this here.
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
Messages.showErrorDialog("Module '" +
selectedBundle.getName() +
"' has no OSGi facet, but should have. Please re-add the OSGi facet to this module.",
"Error");
}
});
}
catch (Exception e) {
// it's ok.
}
_selectedBundles = null;
return;
}
selectedBundle.setBundleUrl(new URL("file", "/", BundleCompiler.getJarFileName(module)).toString());
// add all the dependencies of the bundle
String[] depUrls = BundleCompiler.bundlifyLibraries(module, progressIndicator, DummyCompileContext.getInstance());
for (String depUrl : depUrls) {
SelectedBundle dependency = new SelectedBundle("Dependency", depUrl, SelectedBundle.BundleType.PlainLibrary);
selectedBundles.add(dependency);
}
selectedBundles.add(selectedBundle);
}
catch (MalformedURLException e) {
throw new IllegalStateException(e); // should not happen...
}
}
else {
if (selectedBundles.contains(selectedBundle)) {
// if the user selected a dependency as runnable library, we need to replace the dependency with
// the runnable library part
selectedBundles.remove(selectedBundle);
}
selectedBundles.add(selectedBundle);
}
}
HashMap<String, SelectedBundle> finalList = new HashMap<String, SelectedBundle>();
// filter out bundles which have the same symbolic name
for (SelectedBundle selectedBundle : selectedBundles) {
String name = CachingBundleInfoProvider.getBundleSymbolicName(selectedBundle.getBundleUrl());
String version = CachingBundleInfoProvider.getBundleVersions(selectedBundle.getBundleUrl());
String key = name + version;
if (!finalList.containsKey(key)) {
finalList.put(key, selectedBundle);
}
}
Collection<SelectedBundle> selectedBundleCollection = finalList.values();
_selectedBundles = selectedBundleCollection.toArray(new SelectedBundle[selectedBundleCollection.size()]);
Arrays.sort(_selectedBundles, new StartLevelComparator());
}
});
}
return _selectedBundles;
}
@NotNull
protected OSProcessHandler startProcess() throws ExecutionException {
// run any final configuration steps
SelectedBundle[] bundles = getSelectedBundles();
runner.runCustomInstallationSteps(bundles);
OSProcessHandler handler = super.startProcess();
handler.addProcessListener(new ProcessAdapter() {
public void processTerminated(ProcessEvent event) {
// make sure the runner is disposed when the process exits (so we get rid of the temp folders)
Disposer.dispose(runner);
}
});
return handler;
}
/**
* Comparator for sorting bundles by their start level.
*
* @author <a href="mailto:janthomae@janthomae.de">Jan Thomä</a>
* @version $Id:$
*/
public static class StartLevelComparator implements Comparator<SelectedBundle> {
public int compare(SelectedBundle selectedBundle, SelectedBundle selectedBundle2) {
return selectedBundle.getStartLevel() - selectedBundle2.getStartLevel();
}
}
}