Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: e15f2c65385f924f0ea52604e5d8706e0da471a6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
/*******************************************************************************
 * Copyright (c) 2006, 2017 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM - Initial API and implementation
 *******************************************************************************/
package org.eclipse.equinox.internal.p2.jarprocessor;

import java.io.*;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.zip.ZipException;
import org.eclipse.internal.provisional.equinox.p2.jarprocessor.JarProcessor;

/**
 * @author aniefer@ca.ibm.com
 *
 */
public class Utils {
	public static final String MARK_FILE_NAME = "META-INF/eclipse.inf"; //$NON-NLS-1$

	/*
	 * Properties found in outer pack.properties file
	 */
	//comma separated list of jars to exclude from sigining
	public static final String SIGN_EXCLUDES = "sign.excludes"; //$NON-NLS-1$
	//comma separated list of jars to exlclude from packing
	public static final String PACK_EXCLUDES = "pack.excludes"; //$NON-NLS-1$
	//Suffix used when specifying arguments to use when running pack200 on a jar
	public static final String PACK_ARGS_SUFFIX = ".pack.args"; //$NON-NLS-1$

	/*
	 * Properties found in both pack.properties and eclipse.inf
	 */
	//	Default arguments to use when running pack200.
	// Affects all jars when specified in pack.properties, affects children when specified in eclipse.inf
	public static final String DEFAULT_PACK_ARGS = "pack200.default.args"; //$NON-NLS-1$

	/*
	 * Properties found in eclipse.inf file
	 */
	//This jar has been conditioned with pack200
	public static final String MARK_PROPERTY = "pack200.conditioned"; //$NON-NLS-1$
	//Exclude this jar from processing
	public static final String MARK_EXCLUDE = "jarprocessor.exclude"; //$NON-NLS-1$
	//Exclude this jar from pack200
	public static final String MARK_EXCLUDE_PACK = "jarprocessor.exclude.pack"; //$NON-NLS-1$
	//Exclude this jar from signing
	public static final String MARK_EXCLUDE_SIGN = "jarprocessor.exclude.sign"; //$NON-NLS-1$
	//Exclude this jar's children from processing
	public static final String MARK_EXCLUDE_CHILDREN = "jarprocessor.exclude.children"; //$NON-NLS-1$
	//Exclude this jar's children from pack200
	public static final String MARK_EXCLUDE_CHILDREN_PACK = "jarprocessor.exclude.children.pack"; //$NON-NLS-1$
	//Exclude this jar's children from signing
	public static final String MARK_EXCLUDE_CHILDREN_SIGN = "jarprocessor.exclude.children.sign"; //$NON-NLS-1$
	//Arguments used in pack200 for this jar
	public static final String PACK_ARGS = "pack200.args"; //$NON-NLS-1$

	public static final String PACK200_PROPERTY = "org.eclipse.update.jarprocessor.pack200"; //$NON-NLS-1$
	public static final String JRE = "@jre"; //$NON-NLS-1$
	public static final String PATH = "@path"; //$NON-NLS-1$
	public static final String NONE = "@none"; //$NON-NLS-1$

	public static final String PACKED_SUFFIX = ".pack.gz"; //$NON-NLS-1$
	public static final String JAR_SUFFIX = ".jar"; //$NON-NLS-1$

	public static final FileFilter JAR_FILTER = pathname -> pathname.isFile() && pathname.getName().endsWith(".jar"); //$NON-NLS-1$

	public static final FileFilter PACK_GZ_FILTER = pathname -> pathname.isFile() && pathname.getName().endsWith(JarProcessor.PACKED_SUFFIX);

	public static void close(Object stream) {
		if (stream != null) {
			try {
				if (stream instanceof InputStream)
					((InputStream) stream).close();
				else if (stream instanceof OutputStream)
					((OutputStream) stream).close();
				else if (stream instanceof JarFile)
					((JarFile) stream).close();
			} catch (IOException e) {
				//ignore
			}
		}
	}

	/**
	 * get the set of commands to try to execute pack/unpack 
	 * @param cmd the command, either "pack200" or "unpack200"
	 * @return String [] or null
	 */
	public static String[] getPack200Commands(String cmd) {
		String[] locations = null;
		String prop = System.getProperty(PACK200_PROPERTY);
		String javaHome = System.getProperty("java.home"); //$NON-NLS-1$
		if (NONE.equals(prop)) {
			return null;
		} else if (JRE.equals(prop)) {
			locations = new String[] {javaHome + "/bin/" + cmd}; //$NON-NLS-1$
		} else if (PATH.equals(prop)) {
			locations = new String[] {cmd};
		} else if (prop == null) {
			locations = new String[] {javaHome + "/bin/" + cmd, cmd}; //$NON-NLS-1$ 
		} else {
			locations = new String[] {prop + "/" + cmd}; //$NON-NLS-1$
		}
		return locations;
	}

	/**
	 * Transfers all available bytes from the given input stream to the given
	 * output stream. Closes both streams if close == true, regardless of failure. 
	 * Flushes the destination stream if close == false
	 * 
	 * @param source
	 * @param destination
	 * @param close 
	 * @throws IOException
	 */
	public static void transferStreams(InputStream source, OutputStream destination, boolean close) throws IOException {
		source = new BufferedInputStream(source);
		destination = new BufferedOutputStream(destination);
		try {
			byte[] buffer = new byte[8192];
			while (true) {
				int bytesRead = -1;
				if ((bytesRead = source.read(buffer)) == -1)
					break;
				destination.write(buffer, 0, bytesRead);
			}
		} finally {
			if (close) {
				close(source);
				close(destination);
			} else {
				destination.flush();
			}
		}
	}

	/**
	 * Deletes all the files and directories from the given root down (inclusive).
	 * Returns false if we could not delete some file or an exception occurred
	 * at any point in the deletion.
	 * Even if an exception occurs, a best effort is made to continue deleting.
	 */
	public static boolean clear(java.io.File root) {
		boolean result = clearChildren(root);
		try {
			if (root.exists())
				result &= root.delete();
		} catch (Exception e) {
			result = false;
		}
		return result;
	}

	/**
	 * Deletes all the files and directories from the given root down, except for 
	 * the root itself.
	 * Returns false if we could not delete some file or an exception occurred
	 * at any point in the deletion.
	 * Even if an exception occurs, a best effort is made to continue deleting.
	 */
	public static boolean clearChildren(java.io.File root) {
		boolean result = true;
		if (root.isDirectory()) {
			String[] list = root.list();
			// for some unknown reason, list() can return null.  
			// Just skip the children If it does.
			if (list != null)
				for (int i = 0; i < list.length; i++)
					result &= clear(new java.io.File(root, list[i]));
		}
		return result;
	}

	public static Set<String> getPackExclusions(Properties properties) {
		if (properties == null)
			return Collections.emptySet();

		String packExcludes = properties.getProperty(PACK_EXCLUDES);
		if (packExcludes != null) {
			String[] excludes = toStringArray(packExcludes, ","); //$NON-NLS-1$
			Set<String> packExclusions = new HashSet<>();
			for (int i = 0; i < excludes.length; i++) {
				packExclusions.add(excludes[i]);
			}
			return packExclusions;
		}
		return Collections.emptySet();
	}

	public static Set<String> getSignExclusions(Properties properties) {
		if (properties == null)
			return Collections.emptySet();
		String signExcludes = properties.getProperty(SIGN_EXCLUDES);
		if (signExcludes != null) {
			String[] excludes = toStringArray(signExcludes, ","); //$NON-NLS-1$
			Set<String> signExclusions = new HashSet<>();
			for (int i = 0; i < excludes.length; i++) {
				signExclusions.add(excludes[i]);
			}
			return signExclusions;
		}
		return Collections.emptySet();
	}

	public static String concat(String[] array) {
		StringBuffer buffer = new StringBuffer();
		for (int i = 0; i < array.length; i++) {
			if (i > 0)
				buffer.append(' ');
			buffer.append(array[i]);
		}
		return buffer.toString();
	}

	public static String[] toStringArray(String input, String separator) {
		StringTokenizer tokenizer = new StringTokenizer(input, separator);
		int count = tokenizer.countTokens();
		String[] result = new String[count];
		for (int i = 0; i < count; i++) {
			result[i] = tokenizer.nextToken().trim();
		}
		return result;
	}

	/**
	 * Get the properties from the eclipse.inf file from the given jar.  If the file is not a jar, null is returned.
	 * If the file is a jar, but does not contain an eclipse.inf file, an empty Properties object is returned.
	 * @param jarFile
	 * @return The eclipse.inf properties for the given jar file
	 */
	public static Properties getEclipseInf(File jarFile, boolean verbose) {
		if (jarFile == null || !jarFile.exists()) {
			if (verbose)
				System.out.println("Failed to obtain eclipse.inf due to missing jar file: " + jarFile); //$NON-NLS-1$
			return null;
		}
		JarFile jar = null;
		try {
			jar = new JarFile(jarFile, false);
			JarEntry mark = jar.getJarEntry(MARK_FILE_NAME);
			if (mark != null) {
				try (InputStream in = jar.getInputStream(mark)) {
					Properties props = new Properties();
					props.load(in);
					return props;
				}
			}
			return new Properties();
		} catch (ZipException e) {
			//not a jar, don't bother logging this.
			return null;
		} catch (IOException e) {
			if (verbose) {
				System.out.println("Failed to obtain eclipse.inf due to IOException: " + jarFile); //$NON-NLS-1$
				e.printStackTrace();
			}
			return null;
		} finally {
			close(jar);
		}
	}

	public static boolean shouldSkipJar(File input, boolean processAll, boolean verbose) {
		Properties inf = getEclipseInf(input, verbose);
		if (inf == null) {
			//not a jar, could be a pack.gz
			return false;
		}
		String exclude = inf.getProperty(MARK_EXCLUDE);

		//was marked as exclude, we should skip
		if (exclude != null && Boolean.parseBoolean(exclude))
			return true;

		//process all was set, don't skip
		if (processAll)
			return false;

		//otherwise, we skip if not marked marked
		String marked = inf.getProperty(MARK_PROPERTY);
		return !Boolean.parseBoolean(marked);
	}

	/**
	 * Stores the given properties in the output stream.  We store the properties 
	 * in sorted order so that the signing hash doesn't change if the properties didn't change. 
	 * @param props
	 * @param stream
	 */
	public static void storeProperties(Properties props, OutputStream stream) {
		PrintStream printStream = new PrintStream(stream);
		printStream.print("#Processed using Jarprocessor\n"); //$NON-NLS-1$
		SortedMap<Object, Object> sorted = new TreeMap<>(props);
		for (Iterator<Object> iter = sorted.keySet().iterator(); iter.hasNext();) {
			String key = (String) iter.next();
			printStream.print(key);
			printStream.print(" = "); //$NON-NLS-1$
			printStream.print(sorted.get(key));
			printStream.print("\n"); //$NON-NLS-1$

		}
		printStream.flush();
	}
}

Back to the top