1 /*******************************************************************************
2  *  Copyright (c) 2006, 2013 IBM Corporation and others.
3  *
4  *  This program and the accompanying materials
5  *  are made available under the terms of the Eclipse Public License 2.0
6  *  which accompanies this distribution, and is available at
7  *  https://www.eclipse.org/legal/epl-2.0/
8  *
9  *  SPDX-License-Identifier: EPL-2.0
10  *
11  *  Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.pde.internal.core.exports;
15 
16 import java.io.File;
17 import java.net.MalformedURLException;
18 import java.util.Map;
19 import java.util.regex.Pattern;
20 import org.eclipse.core.resources.IContainer;
21 import org.eclipse.core.resources.IFolder;
22 import org.eclipse.core.resources.IResource;
23 import org.eclipse.core.runtime.CoreException;
24 import org.eclipse.core.runtime.IProgressMonitor;
25 import org.eclipse.core.runtime.IStatus;
26 import org.eclipse.core.runtime.Path;
27 import org.eclipse.core.runtime.URIUtil;
28 import org.eclipse.core.runtime.jobs.MultiRule;
29 import org.eclipse.pde.internal.build.IBuildPropertiesConstants;
30 import org.eclipse.pde.internal.build.IXMLConstants;
31 import org.eclipse.pde.internal.core.P2Utils;
32 import org.eclipse.pde.internal.core.PDECore;
33 import org.eclipse.pde.internal.core.PDECoreMessages;
34 import org.eclipse.pde.internal.core.ifeature.IFeature;
35 import org.eclipse.pde.internal.core.ifeature.IFeatureModel;
36 import org.eclipse.pde.internal.core.isite.ISiteDescription;
37 import org.eclipse.pde.internal.core.isite.ISiteFeature;
38 import org.eclipse.pde.internal.core.isite.ISiteModel;
39 import org.eclipse.pde.internal.core.site.WorkspaceSiteModel;
40 import org.eclipse.pde.internal.core.util.PatternConstructor;
41 import org.osgi.framework.Version;
42 
43 /**
44  * Performs a site build operation that will build any features needed by the site and generate
45  * p2 metadata for those features.
46  *
47  * @see FeatureBasedExportOperation
48  * @see FeatureExportOperation
49  */
50 public class SiteBuildOperation extends FeatureBasedExportOperation {
51 
52 	private long fBuildTime;
53 
54 	private final IFeatureModel[] fFeatureModels;
55 	private final ISiteModel fSiteModel;
56 	private final IContainer fSiteContainer;
57 
SiteBuildOperation(IFeatureModel[] features, ISiteModel site, String jobName)58 	public SiteBuildOperation(IFeatureModel[] features, ISiteModel site, String jobName) {
59 		super(getInfo(features, site), jobName);
60 		fFeatureModels = features;
61 		fSiteModel = site;
62 		fSiteContainer = site.getUnderlyingResource().getParent();
63 		setRule(MultiRule.combine(fSiteContainer.getProject(), getRule()));
64 	}
65 
getInfo(IFeatureModel[] models, ISiteModel siteModel)66 	private static FeatureExportInfo getInfo(IFeatureModel[] models, ISiteModel siteModel) {
67 		FeatureExportInfo info = new FeatureExportInfo();
68 		info.useJarFormat = true;
69 		info.toDirectory = true;
70 		info.allowBinaryCycles = true;
71 		info.destinationDirectory = siteModel.getUnderlyingResource().getParent().getLocation().toOSString();
72 		info.items = models;
73 		return info;
74 	}
75 
76 	@Override
run(IProgressMonitor monitor)77 	protected IStatus run(IProgressMonitor monitor) {
78 		fBuildTime = System.currentTimeMillis();
79 		IStatus status = super.run(monitor);
80 		try {
81 			fSiteContainer.refreshLocal(IResource.DEPTH_INFINITE, monitor);
82 			updateSiteFeatureVersions();
83 		} catch (CoreException ce) {
84 			return ce.getStatus();
85 		}
86 		return status;
87 	}
88 
updateSiteFeatureVersions()89 	private void updateSiteFeatureVersions() throws CoreException {
90 		for (IFeatureModel featureModel : fFeatureModels) {
91 			IFeature feature = featureModel.getFeature();
92 			Version pvi = Version.parseVersion(feature.getVersion());
93 
94 			if ("qualifier".equals(pvi.getQualifier())) { //$NON-NLS-1$
95 				String newVersion = findBuiltVersion(feature.getId(), pvi.getMajor(), pvi.getMinor(), pvi.getMicro());
96 				if (newVersion == null) {
97 					continue;
98 				}
99 				ISiteFeature reVersionCandidate = findSiteFeature(feature, pvi);
100 				if (reVersionCandidate != null) {
101 					reVersionCandidate.setVersion(newVersion);
102 					reVersionCandidate.setURL("features/" + feature.getId() + "_" //$NON-NLS-1$ //$NON-NLS-2$
103 							+ newVersion + ".jar"); //$NON-NLS-1$
104 				}
105 			}
106 		}
107 		((WorkspaceSiteModel) fSiteModel).save();
108 	}
109 
findSiteFeature(IFeature feature, Version pvi)110 	private ISiteFeature findSiteFeature(IFeature feature, Version pvi) {
111 		ISiteFeature reversionCandidate = null;
112 		// first see if version with qualifier being qualifier is present among
113 		// site features
114 		ISiteFeature[] siteFeatures = fSiteModel.getSite().getFeatures();
115 		for (ISiteFeature siteFeature : siteFeatures) {
116 			if (siteFeature.getId().equals(feature.getId()) && siteFeature.getVersion().equals(feature.getVersion())) {
117 				return siteFeature;
118 			}
119 		}
120 		String highestQualifier = null;
121 		// then find feature with the highest qualifier
122 		for (ISiteFeature siteFeature : siteFeatures) {
123 			if (siteFeature.getId().equals(feature.getId())) {
124 				Version candidatePvi = Version.parseVersion(siteFeature.getVersion());
125 				if (pvi.getMajor() == candidatePvi.getMajor() && pvi.getMinor() == candidatePvi.getMinor() && pvi.getMicro() == candidatePvi.getMicro()) {
126 					if (reversionCandidate == null || candidatePvi.getQualifier().compareTo(highestQualifier) > 0) {
127 						reversionCandidate = siteFeature;
128 						highestQualifier = candidatePvi.getQualifier();
129 					}
130 				}
131 			}
132 		}
133 		return reversionCandidate;
134 	}
135 
136 	/**
137 	 * Finds the highest version from feature jars. ID and version components
138 	 * are constant. Qualifier varies
139 	 *
140 	 * @param id
141 	 * @param major
142 	 * @param minor
143 	 * @param service
144 	 */
findBuiltVersion(String id, int major, int minor, int service)145 	private String findBuiltVersion(String id, int major, int minor, int service) {
146 		IFolder featuresFolder = fSiteContainer.getFolder(new Path("features")); //$NON-NLS-1$
147 		if (!featuresFolder.exists()) {
148 			return null;
149 		}
150 		IResource[] featureJars = null;
151 		try {
152 			featureJars = featuresFolder.members();
153 		} catch (CoreException ce) {
154 			return null;
155 		}
156 		Pattern pattern = PatternConstructor.createPattern(id + "_" //$NON-NLS-1$
157 				+ major + "." //$NON-NLS-1$
158 				+ minor + "." //$NON-NLS-1$
159 				+ service + "*.jar", true); //$NON-NLS-1$
160 		// finding the newest feature archive
161 		String newestName = null;
162 		long newestTime = 0;
163 		for (IResource featureJar : featureJars) {
164 			File file = new File(featureJar.getLocation().toOSString());
165 			long jarTime = file.lastModified();
166 			String jarName = featureJar.getName();
167 
168 			if (jarTime < fBuildTime) {
169 				continue;
170 			}
171 			if (jarTime <= newestTime) {
172 				continue;
173 			}
174 			if (pattern.matcher(jarName).matches()) {
175 				newestName = featureJar.getName();
176 				newestTime = jarTime;
177 			}
178 		}
179 		if (newestName == null) {
180 			return null;
181 		}
182 
183 		return newestName.substring(id.length() + 1, newestName.length() - 4);
184 	}
185 
186 	@Override
createPostProcessingFiles()187 	protected void createPostProcessingFiles() {
188 		createPostProcessingFile(new File(fFeatureLocation, FEATURE_POST_PROCESSING));
189 		createPostProcessingFile(new File(fFeatureLocation, PLUGIN_POST_PROCESSING));
190 	}
191 
192 	@Override
publishingP2Metadata()193 	protected boolean publishingP2Metadata() {
194 		return true;
195 	}
196 
197 	@Override
setP2MetaDataProperties(Map<String, String> map)198 	protected void setP2MetaDataProperties(Map<String, String> map) {
199 		if (fInfo.toDirectory) {
200 			map.put(IXMLConstants.TARGET_P2_METADATA, IBuildPropertiesConstants.TRUE);
201 			map.put(IBuildPropertiesConstants.PROPERTY_P2_FLAVOR, P2Utils.P2_FLAVOR_DEFAULT);
202 			map.put(IBuildPropertiesConstants.PROPERTY_P2_PUBLISH_ARTIFACTS, IBuildPropertiesConstants.TRUE);
203 			map.put(IBuildPropertiesConstants.PROPERTY_P2_FINAL_MODE_OVERRIDE, IBuildPropertiesConstants.TRUE);
204 			map.put(IBuildPropertiesConstants.PROPERTY_P2_COMPRESS, IBuildPropertiesConstants.TRUE);
205 			map.put(IBuildPropertiesConstants.PROPERTY_P2_GATHERING, Boolean.toString(publishingP2Metadata()));
206 			IResource siteXML = fSiteModel.getUnderlyingResource();
207 			if (siteXML.exists() && siteXML.getLocationURI() != null) {
208 				map.put(IBuildPropertiesConstants.PROPERTY_P2_CATEGORY_SITE, URIUtil.toUnencodedString(siteXML.getLocationURI()));
209 			}
210 
211 			ISiteDescription description = fSiteModel.getSite().getDescription();
212 			String name = description != null && description.getName() != null && description.getName().length() > 0 ? description.getName() : PDECoreMessages.SiteBuildOperation_0;
213 			map.put(IBuildPropertiesConstants.PROPERTY_P2_METADATA_REPO_NAME, name);
214 			map.put(IBuildPropertiesConstants.PROPERTY_P2_ARTIFACT_REPO_NAME, name);
215 
216 			try {
217 				String destination = new File(fBuildTempMetadataLocation).toURL().toString();
218 				map.put(IBuildPropertiesConstants.PROPERTY_P2_BUILD_REPO, destination);
219 			} catch (MalformedURLException e) {
220 				PDECore.log(e);
221 			}
222 		}
223 	}
224 
225 }
226