blob: 868d31c6b3e8522bd72731202f3c39f9f9f338ef [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2012 Code 9 and others. All rights reserved. This
* program and the accompanying materials are made available under the terms of
* the Eclipse Public License v1.0 which accompanies this distribution, and is
* available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Code 9 - initial API and implementation
* IBM - ongoing development
* SAP AG - ongoing development
******************************************************************************/
package org.eclipse.equinox.p2.publisher.actions;
import java.io.*;
import java.net.URL;
import java.util.*;
import org.eclipse.core.runtime.*;
import org.eclipse.equinox.internal.p2.core.helpers.CollectionUtils;
import org.eclipse.equinox.internal.p2.metadata.ArtifactKey;
import org.eclipse.equinox.internal.p2.publisher.Activator;
import org.eclipse.equinox.internal.p2.publisher.Messages;
import org.eclipse.equinox.p2.metadata.*;
import org.eclipse.equinox.p2.metadata.MetadataFactory.InstallableUnitDescription;
import org.eclipse.equinox.p2.metadata.MetadataFactory.InstallableUnitFragmentDescription;
import org.eclipse.equinox.p2.metadata.Version;
import org.eclipse.equinox.p2.metadata.VersionRange;
import org.eclipse.equinox.p2.publisher.*;
import org.eclipse.equinox.p2.repository.artifact.IArtifactDescriptor;
import org.eclipse.equinox.spi.p2.publisher.PublisherHelper;
import org.eclipse.osgi.util.ManifestElement;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.*;
public class JREAction extends AbstractPublisherAction {
private static final String DEFAULT_JRE_NAME = "a.jre"; //$NON-NLS-1$
private static final Version DEFAULT_JRE_VERSION = Version.parseVersion("1.6"); //$NON-NLS-1$
private static final String DEFAULT_PROFILE = "JavaSE-1.6"; //$NON-NLS-1$
private static final String PROFILE_LOCATION = "jre.action.profile.location"; //$NON-NLS-1$
private static final String PROFILE_NAME = "osgi.java.profile.name"; //$NON-NLS-1$
private static final String PROFILE_TARGET_VERSION = "org.eclipse.jdt.core.compiler.codegen.targetPlatform"; //$NON-NLS-1$
private static final String PROFILE_SYSTEM_PACKAGES = "org.osgi.framework.system.packages"; //$NON-NLS-1$
public static final String NAMESPACE_OSGI_EE = "osgi.ee"; //$NON-NLS-1$
private File jreLocation;
private String environment;
private Map<String, String> profileProperties;
private MultiStatus resultStatus;
public JREAction(File location) {
this.jreLocation = location;
}
public JREAction(String environment) {
this.environment = environment;
}
@Override
public IStatus perform(IPublisherInfo publisherInfo, IPublisherResult results, IProgressMonitor monitor) {
String problemMessage = NLS.bind(Messages.message_problemsWhilePublishingEE, jreLocation != null ? jreLocation : environment);
resultStatus = new MultiStatus(Activator.ID, 0, problemMessage, null);
initialize(publisherInfo);
IArtifactDescriptor artifact = createJREData(results);
if (artifact != null)
publishArtifact(artifact, new File[] {jreLocation}, null, publisherInfo, createRootPrefixComputer(jreLocation));
if (resultStatus.isOK())
return Status.OK_STATUS;
return resultStatus;
}
private static Status newErrorStatus(String message, Exception exception) {
return new Status(IStatus.ERROR, Activator.ID, message, exception);
}
private static Status newWarningStatus(String message) {
return new Status(IStatus.WARNING, Activator.ID, message, null);
}
/**
* Creates IUs and artifact descriptors for the JRE. The resulting IUs are added
* to the given set, and the resulting artifact descriptor, if any, is returned.
* If the jreLocation is <code>null</code>, default information is generated.
*/
protected IArtifactDescriptor createJREData(IPublisherResult results) {
InstallableUnitDescription iu = new MetadataFactory.InstallableUnitDescription();
iu.setSingleton(false);
iu.setId(DEFAULT_JRE_NAME);
iu.setVersion(DEFAULT_JRE_VERSION);
iu.setTouchpointType(PublisherHelper.TOUCHPOINT_NATIVE);
generateJREIUData(iu);
InstallableUnitFragmentDescription cu = new InstallableUnitFragmentDescription();
String configId = "config." + iu.getId();//$NON-NLS-1$
cu.setId(configId);
cu.setVersion(iu.getVersion());
VersionRange range = iu.getVersion() == Version.emptyVersion ? VersionRange.emptyRange : new VersionRange(iu.getVersion(), true, Version.MAX_VERSION, true);
cu.setHost(new IRequirement[] {MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, iu.getId(), range, null, false, false)});
cu.setProperty(InstallableUnitDescription.PROP_TYPE_FRAGMENT, Boolean.TRUE.toString());
cu.setCapabilities(new IProvidedCapability[] {PublisherHelper.createSelfCapability(configId, iu.getVersion())});
cu.setTouchpointType(PublisherHelper.TOUCHPOINT_NATIVE);
Map<String, String> touchpointData = new HashMap<>();
if (jreLocation == null || !jreLocation.isDirectory()) {
touchpointData.put("install", ""); //$NON-NLS-1$ //$NON-NLS-2$
cu.addTouchpointData(MetadataFactory.createTouchpointData(touchpointData));
results.addIU(MetadataFactory.createInstallableUnit(iu), IPublisherResult.ROOT);
results.addIU(MetadataFactory.createInstallableUnit(cu), IPublisherResult.ROOT);
return null;
}
//Generate artifact for JRE
IArtifactKey key = new ArtifactKey(PublisherHelper.BINARY_ARTIFACT_CLASSIFIER, iu.getId(), iu.getVersion());
iu.setArtifacts(new IArtifactKey[] {key});
results.addIU(MetadataFactory.createInstallableUnit(iu), IPublisherResult.ROOT);
//Create config info for the CU
String configurationData = "unzip(source:@artifact, target:${installFolder});"; //$NON-NLS-1$
touchpointData.put("install", configurationData); //$NON-NLS-1$
String unConfigurationData = "cleanupzip(source:@artifact, target:${installFolder});"; //$NON-NLS-1$
touchpointData.put("uninstall", unConfigurationData); //$NON-NLS-1$
cu.addTouchpointData(MetadataFactory.createTouchpointData(touchpointData));
results.addIU(MetadataFactory.createInstallableUnit(cu), IPublisherResult.ROOT);
//Create the artifact descriptor
return PublisherHelper.createArtifactDescriptor(info, key, jreLocation);
}
private List<IProvidedCapability> generateJRECapability(String id, Version version) {
if (profileProperties == null)
return Collections.emptyList();
List<IProvidedCapability> result = new ArrayList<>();
result.add(PublisherHelper.createSelfCapability(id, version));
generateProvidedPackages(result);
generateOsgiEESystemCapabilities(result);
return result;
}
private void generateProvidedPackages(List<IProvidedCapability> result) {
String packages = profileProperties.get(PROFILE_SYSTEM_PACKAGES);
if (packages != null && (packages.trim().length() > 0)) {
try {
ManifestElement[] jrePackages = ManifestElement.parseHeader(PROFILE_SYSTEM_PACKAGES, packages);
for (int i = 0; i < jrePackages.length; i++) {
String packageName = jrePackages[i].getValue();
Version packageVersion = Version.create(jrePackages[i].getAttribute("version")); //$NON-NLS-1$
result.add(MetadataFactory.createProvidedCapability(PublisherHelper.CAPABILITY_NS_JAVA_PACKAGE, packageName, packageVersion));
}
} catch (BundleException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
void generateOsgiEESystemCapabilities(List<IProvidedCapability> result) {
String message = NLS.bind(Messages.message_problemsWhileParsingProfileProperty, Constants.FRAMEWORK_SYSTEMCAPABILITIES);
MultiStatus parsingStatus = new MultiStatus(Activator.ID, 0, message, null);
String systemCapabilities = profileProperties.get(Constants.FRAMEWORK_SYSTEMCAPABILITIES);
parseSystemCapabilities(systemCapabilities, parsingStatus, result);
// result contains the valid entries, parsingStatus the invalid entries
if (!parsingStatus.isOK())
resultStatus.add(parsingStatus);
}
static void parseSystemCapabilities(String systemCapabilities, MultiStatus parsingStatus, List<IProvidedCapability> parsingResult) {
if (systemCapabilities == null || (systemCapabilities.trim().length() == 0)) {
return;
}
try {
ManifestElement[] eeEntries = ManifestElement.parseHeader(Constants.FRAMEWORK_SYSTEMCAPABILITIES, systemCapabilities);
parseSystemCapabilities(eeEntries, parsingStatus, parsingResult);
} catch (BundleException e) {
parsingStatus.add(newErrorStatus(e.getLocalizedMessage(), e));
}
}
private static void parseSystemCapabilities(ManifestElement[] systemCapabilities, MultiStatus parsingStatus, List<IProvidedCapability> parsingResult) {
for (int capabilityIx = 0; capabilityIx < systemCapabilities.length; capabilityIx++) {
ManifestElement systemCapability = systemCapabilities[capabilityIx];
// this is general manifest syntax: a "manifest element" can have multiple "value components" -> all attributes apply to each value component (=namespace)
String[] namespaces = systemCapability.getValueComponents();
for (int namespaceIx = 0; namespaceIx < namespaces.length; namespaceIx++) {
String namespace = namespaces[namespaceIx];
if (NAMESPACE_OSGI_EE.equals(namespace)) { // this is the OSGi capability namespace "osgi.ee"
parseEECapability(systemCapability, parsingStatus, parsingResult);
} else {
parsingStatus.add(newWarningStatus(NLS.bind(Messages.message_eeIgnoringNamespace, namespace)));
continue;
}
}
}
}
private static void parseEECapability(ManifestElement eeCapability, MultiStatus parsingStatus, List<IProvidedCapability> parsingResult) {
String eeName = eeCapability.getAttribute(NAMESPACE_OSGI_EE); // this is an attribute required for capabilities in the "osgi.ee" namespace
if (eeName == null) {
parsingStatus.add(newErrorStatus(NLS.bind(Messages.message_eeMissingNameAttribute, eeCapability), null));
return;
}
String[] eeVersions = parseEECapabilityVersion(eeCapability, parsingStatus);
if (eeVersions == null) {
// status was already updated by parse method
return;
}
for (int versionIx = 0; versionIx < eeVersions.length; versionIx++) {
String rawVersion = eeVersions[versionIx];
try {
Version parsedVersion = Version.parseVersion(rawVersion);
// complete record -> store
Map<String, Object> capAttrs = new HashMap<String, Object>();
capAttrs.put(NAMESPACE_OSGI_EE, eeName);
capAttrs.put("version", parsedVersion);
parsingResult.add(MetadataFactory.createProvidedCapability(NAMESPACE_OSGI_EE, capAttrs));
} catch (IllegalArgumentException e) {
parsingStatus.add(newErrorStatus(NLS.bind(Messages.message_eeInvalidVersionAttribute, rawVersion), e));
}
}
}
private static String[] parseEECapabilityVersion(ManifestElement eeCapability, MultiStatus parsingStatus) {
String singleVersion = eeCapability.getAttribute("version:Version"); //$NON-NLS-1$
String[] multipleVersions = ManifestElement.getArrayFromList(eeCapability.getAttribute("version:List<Version>")); //$NON-NLS-1$
if (singleVersion == null && multipleVersions == null) {
parsingStatus.add(newErrorStatus(NLS.bind(Messages.message_eeMissingVersionAttribute, eeCapability), null));
return null;
} else if (singleVersion == null) {
return multipleVersions;
} else if (multipleVersions == null) {
return new String[] {singleVersion};
} else {
parsingStatus.add(newErrorStatus(NLS.bind(Messages.message_eeDuplicateVersionAttribute, eeCapability), null));
return null;
}
}
private void generateJREIUData(InstallableUnitDescription iu) {
if (profileProperties == null || profileProperties.size() == 0)
return; //got nothing
String profileLocation = profileProperties.get(PROFILE_LOCATION);
String profileName = profileLocation != null ? new Path(profileLocation).lastSegment() : profileProperties.get(PROFILE_NAME);
if (profileName.endsWith(".profile")) //$NON-NLS-1$
profileName = profileName.substring(0, profileName.length() - 8);
Version version = null;
int idx = profileName.indexOf('-');
if (idx != -1) {
try {
version = Version.parseVersion(profileName.substring(idx + 1));
} catch (IllegalArgumentException e) {
//ignore
}
profileName = profileName.substring(0, idx);
}
if (version == null) {
try {
String targetVersion = profileProperties.get(PROFILE_TARGET_VERSION);
version = targetVersion != null ? Version.parseVersion(targetVersion) : null;
} catch (IllegalArgumentException e) {
//ignore
}
}
if (version == null)
version = DEFAULT_JRE_VERSION;
iu.setVersion(version);
profileName = profileName.replace('-', '.');
profileName = profileName.replace('/', '.');
profileName = profileName.replace('_', '.');
iu.setId("a.jre." + profileName.toLowerCase()); //$NON-NLS-1$
List<IProvidedCapability> capabilities = generateJRECapability(iu.getId(), iu.getVersion());
iu.addProvidedCapabilities(capabilities);
}
private void initialize(IPublisherInfo publisherInfo) {
this.info = publisherInfo;
if (jreLocation != null) {
File javaProfile = null;
if (jreLocation.isDirectory()) {
//Look for a JRE profile file to set version and capabilities
File[] profiles = jreLocation.listFiles((FileFilter) pathname -> pathname.getAbsolutePath().endsWith(".profile"));
if (profiles != null && profiles.length > 0) {
javaProfile = profiles[0];
}
} else if (jreLocation.isFile())
javaProfile = jreLocation;
else
// jreLocation file does not exist
throw new IllegalArgumentException(NLS.bind(Messages.exception_nonExistingJreLocationFile, jreLocation.getAbsolutePath()));
profileProperties = loadProfile(javaProfile);
}
if (profileProperties == null) {
String profileFile = (environment != null ? environment : DEFAULT_PROFILE).replace('/', '_') + ".profile"; //$NON-NLS-1$
URL profileURL = getResouceFromSystemBundle(profileFile);
profileProperties = loadProfile(profileURL);
}
}
private static URL getResouceFromSystemBundle(String entry) {
// get resource from system bundle which is typically on the Java boot classpath
ClassLoader loader = FrameworkUtil.class.getClassLoader();
return loader == null ? ClassLoader.getSystemResource(entry) : loader.getResource(entry);
}
private Map<String, String> loadProfile(File profileFile) {
if (profileFile == null || !profileFile.exists())
return null;
try {
InputStream stream = new BufferedInputStream(new FileInputStream(profileFile));
Map<String, String> properties = loadProfile(stream);
if (properties != null)
properties.put(PROFILE_LOCATION, profileFile.getAbsolutePath());
return properties;
} catch (FileNotFoundException e) {
//null
}
return null;
}
private Map<String, String> loadProfile(URL profileURL) {
if (profileURL == null)
return null;
try {
InputStream stream = profileURL.openStream();
return loadProfile(stream);
} catch (IOException e) {
//null
}
return null;
}
/**
* Always closes the stream when done
*/
private Map<String, String> loadProfile(InputStream stream) {
if (stream != null) {
try {
return CollectionUtils.loadProperties(stream);
} catch (IOException e) {
return null;
} finally {
try {
stream.close();
} catch (IOException e) {
// error
}
}
}
return null;
}
}