1 /*******************************************************************************
2  * Copyright (c) 2009, 2017 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  *     Manumitting Technologies Inc - bug 437726: wrong error messages opening target definition
14  *******************************************************************************/
15 package org.eclipse.pde.internal.core.target;
16 
17 import java.io.File;
18 import java.io.IOException;
19 import java.util.HashMap;
20 import java.util.Map.Entry;
21 import org.eclipse.core.runtime.CoreException;
22 import org.eclipse.core.runtime.IProgressMonitor;
23 import org.eclipse.core.runtime.IStatus;
24 import org.eclipse.core.runtime.Platform;
25 import org.eclipse.core.runtime.PlatformObject;
26 import org.eclipse.core.runtime.Status;
27 import org.eclipse.core.runtime.SubMonitor;
28 import org.eclipse.core.variables.IStringVariableManager;
29 import org.eclipse.core.variables.VariablesPlugin;
30 import org.eclipse.equinox.internal.provisional.frameworkadmin.ConfigData;
31 import org.eclipse.equinox.internal.provisional.frameworkadmin.FrameworkAdmin;
32 import org.eclipse.equinox.internal.provisional.frameworkadmin.Manipulator;
33 import org.eclipse.pde.core.target.ITargetDefinition;
34 import org.eclipse.pde.core.target.ITargetLocation;
35 import org.eclipse.pde.core.target.TargetBundle;
36 import org.eclipse.pde.core.target.TargetFeature;
37 import org.eclipse.pde.internal.core.PDECore;
38 import org.osgi.framework.Bundle;
39 import org.osgi.framework.BundleException;
40 
41 /**
42  * Common function for bundle containers.
43  *
44  * @since 3.5
45  */
46 public abstract class AbstractBundleContainer extends PlatformObject implements ITargetLocation {
47 
48 	/**
49 	 * Resolved bundles or <code>null</code> if unresolved
50 	 */
51 	protected TargetBundle[] fBundles;
52 
53 	/**
54 	 * List of target features contained in this bundle container or <code>null</code> if unresolved
55 	 */
56 	protected TargetFeature[] fFeatures;
57 
58 	/**
59 	 * Status generated when this container was resolved, possibly <code>null</code>
60 	 */
61 	protected IStatus fResolutionStatus;
62 
63 	/**
64 	 * The Java VM Arguments specified by this bundle container
65 	 */
66 	private String[] fVMArgs;
67 
68 	static private HashMap<AbstractBundleContainer, String[]> hash = new HashMap<>();
69 
70 	/**
71 	 * Resolves any string substitution variables in the given text returning
72 	 * the result.
73 	 *
74 	 * @param text text to resolve
75 	 * @return result of the resolution
76 	 * @throws CoreException if unable to resolve
77 	 */
resolveVariables(String text)78 	protected String resolveVariables(String text) throws CoreException {
79 		IStringVariableManager manager = VariablesPlugin.getDefault().getStringVariableManager();
80 		return manager.performStringSubstitution(text);
81 	}
82 
83 	@Override
isResolved()84 	public final boolean isResolved() {
85 		return fResolutionStatus != null && fResolutionStatus.getSeverity() != IStatus.CANCEL;
86 	}
87 
88 	@Override
resolve(ITargetDefinition definition, IProgressMonitor monitor)89 	public final IStatus resolve(ITargetDefinition definition, IProgressMonitor monitor) {
90 		int resolveBundlesWork = getResolveBundlesWork();
91 		int resolveFeaturesWork = getResolveFeaturesWork();
92 
93 		SubMonitor subMonitor = SubMonitor.convert(monitor, resolveBundlesWork + resolveFeaturesWork);
94 		try {
95 			fResolutionStatus = Status.OK_STATUS;
96 			fBundles = resolveBundles(definition, subMonitor.split(resolveBundlesWork));
97 			fFeatures = resolveFeatures(definition, subMonitor.split(resolveFeaturesWork));
98 			if (subMonitor.isCanceled()) {
99 				fBundles = null;
100 				fResolutionStatus = Status.CANCEL_STATUS;
101 			}
102 		} catch (CoreException e) {
103 			fBundles = new TargetBundle[0];
104 			fFeatures = new TargetFeature[0];
105 			fResolutionStatus = e.getStatus();
106 		} finally {
107 			subMonitor.done();
108 			if (monitor != null) {
109 				monitor.done();
110 			}
111 		}
112 		return fResolutionStatus;
113 	}
114 
115   /**
116    * Can be overridden in subclasses to redistribute the work between {@link #resolveBundles(ITargetDefinition, IProgressMonitor)}
117    * and {@link #resolveFeatures(ITargetDefinition, IProgressMonitor)}.
118    *
119    * @return the value 100, making {@link #resolveFeatures(ITargetDefinition, IProgressMonitor)} consume two thirds of
120    *  the overall work being done in {@link #resolve(ITargetDefinition, IProgressMonitor)}.
121    * @see #getResolveFeaturesWork()
122    */
getResolveBundlesWork()123   protected int getResolveBundlesWork() {
124     return 100;
125   }
126 
127   /**
128    * Can be overridden in subclasses to redistribute the work between {@link #resolveBundles(ITargetDefinition, IProgressMonitor)}
129    * and {@link #resolveFeatures(ITargetDefinition, IProgressMonitor)}.
130    *
131    * @return the value 50, making {@link #resolveFeatures(ITargetDefinition, IProgressMonitor)} consume one third of
132    *  the overall work being done in {@link #resolve(ITargetDefinition, IProgressMonitor)}.
133    * @see #getResolveBundlesWork()
134    */
getResolveFeaturesWork()135   protected int getResolveFeaturesWork() {
136     return 50;
137   }
138 
139 	@Override
getStatus()140 	public IStatus getStatus() {
141 		if (!isResolved()) {
142 			return null;
143 		}
144 		return fResolutionStatus;
145 	}
146 
147 	@Override
getBundles()148 	public final TargetBundle[] getBundles() {
149 		if (isResolved()) {
150 			return fBundles;
151 		}
152 		return null;
153 	}
154 
155 	@Override
getFeatures()156 	public TargetFeature[] getFeatures() {
157 		if (isResolved()) {
158 			return fFeatures;
159 		}
160 		return null;
161 	}
162 
163 	/**
164 	 * Resolves all source and executable bundles in this container
165 	 * <p>
166 	 * Subclasses must implement this method.
167 	 * </p><p>
168 	 * <code>beginTask()</code> and <code>done()</code> will be called on the given monitor by the caller.
169 	 * </p>
170 	 * @param definition target context
171 	 * @param monitor progress monitor
172 	 * @return all source and executable bundles in this container
173 	 * @throws CoreException if an error occurs
174 	 */
resolveBundles(ITargetDefinition definition, IProgressMonitor monitor)175 	protected abstract TargetBundle[] resolveBundles(ITargetDefinition definition, IProgressMonitor monitor) throws CoreException;
176 
177 	/**
178 	 * Collects all of the features in this container.  May return an empty array if {@link #resolveBundles(ITargetDefinition, IProgressMonitor)}
179 	 * has not been called previously.
180 	 * <p>
181 	 * Subclasses must implement this method.
182 	 * </p><p>
183 	 * <code>beginTask()</code> and <code>done()</code> will be called on the given monitor by the caller.
184 	 * </p>
185 	 * @param definition target context
186 	 * @param monitor progress monitor
187 	 * @return all features in this container
188 	 * @throws CoreException if an error occurs
189 	 */
resolveFeatures(ITargetDefinition definition, IProgressMonitor monitor)190 	protected abstract TargetFeature[] resolveFeatures(ITargetDefinition definition, IProgressMonitor monitor) throws CoreException;
191 
192 	/**
193 	 * Returns a string that identifies the type of bundle container.  This type is persisted to xml
194 	 * so that the correct bundle container is created when deserializing the xml.  This type is also
195 	 * used to alter how the containers are presented to the user in the UI.
196 	 *
197 	 * @return string identifier for the type of bundle container.
198 	 */
199 	@Override
getType()200 	public abstract String getType();
201 
202 	/**
203 	 * Returns a path in the local file system to the root of the bundle container.
204 	 * <p>
205 	 * TODO: Ideally we won't need this method. Currently the PDE target platform preferences are
206 	 * based on a home location and additional locations, so we need the information.
207 	 * </p>
208 	 * @param resolve whether to resolve variables in the path
209 	 * @return home location
210 	 * @exception CoreException if unable to resolve the location
211 	 */
212 	@Override
getLocation(boolean resolve)213 	public abstract String getLocation(boolean resolve) throws CoreException;
214 
215 	/**
216 	 * Sets the resolution status to null.  This container will be considered unresolved.
217 	 */
clearResolutionStatus()218 	protected void clearResolutionStatus() {
219 		fResolutionStatus = null;
220 	}
221 
222 	@Override
getVMArguments()223 	public String[] getVMArguments() {
224 		for (Entry<AbstractBundleContainer, String[]> entry : hash.entrySet()) {
225 			if (entry.getKey().equals(this)) {
226 				return entry.getValue();
227 			}
228 		}
229 		String FWK_ADMIN_EQ = "org.eclipse.equinox.frameworkadmin.equinox"; //$NON-NLS-1$
230 		if (fVMArgs == null) {
231 			try {
232 				FrameworkAdmin fwAdmin = PDECore.getDefault().acquireService(FrameworkAdmin.class);
233 				if (fwAdmin == null) {
234 					Bundle fwAdminBundle = Platform.getBundle(FWK_ADMIN_EQ);
235 					if (fwAdminBundle != null) {
236 						fwAdminBundle.start();
237 						fwAdmin = PDECore.getDefault().acquireService(FrameworkAdmin.class);
238 					}
239 				}
240 				if (fwAdmin != null) {
241 					Manipulator manipulator = fwAdmin.getManipulator();
242 					ConfigData configData = new ConfigData(null, null, null, null);
243 
244 					String home = getLocation(true);
245 					manipulator.getLauncherData().setLauncher(new File(home, "eclipse")); //$NON-NLS-1$
246 					File installDirectory = new File(home);
247 //					if (Platform.getOS().equals(Platform.OS_MACOSX))
248 //						installDirectory = new File(installDirectory, "Eclipse.app/Contents/MacOS"); //$NON-NLS-1$
249 					manipulator.getLauncherData().setLauncherConfigLocation(new File(installDirectory, "eclipse.ini")); //$NON-NLS-1$
250 					manipulator.getLauncherData().setHome(new File(home));
251 
252 					manipulator.setConfigData(configData);
253 					manipulator.load();
254 					fVMArgs = manipulator.getLauncherData().getJvmArgs();
255 				}
256 			} catch (BundleException | CoreException | IOException e) {
257 				PDECore.log(e);
258 			}
259 
260 		}
261 		if (fVMArgs == null || fVMArgs.length == 0) {
262 			hash.put(this, null);
263 			return null;
264 		}
265 		hash.put(this, fVMArgs);
266 		return fVMArgs;
267 	}
268 
269 	/**
270 	 * Associate this bundle container with the given target.  This allows for the container and
271 	 * the target to share configuration information etc.
272 	 *
273 	 * @param target the target to which this container is being added.
274 	 */
associateWithTarget(ITargetDefinition target)275 	protected void associateWithTarget(ITargetDefinition target) {
276 		// Do nothing by default
277 	}
278 
279 	@Override
serialize()280 	public String serialize() {
281 		// The default implementation returns null as most containers do not use the new UI
282 		return null;
283 	}
284 
285 }
286