View Javadoc
1   /*
2    * Copyright (C) 2009, Google Inc.
3    * Copyright (C) 2009, Robin Rosenberg <robin.rosenberg@dewire.com>
4    * Copyright (C) 2009, Yann Simon <yann.simon.fr@gmail.com>
5    * Copyright (C) 2012, Daniel Megert <daniel_megert@ch.ibm.com>
6    * and other copyright owners as documented in the project's IP log.
7    *
8    * This program and the accompanying materials are made available
9    * under the terms of the Eclipse Distribution License v1.0 which
10   * accompanies this distribution, is reproduced below, and is
11   * available at http://www.eclipse.org/org/documents/edl-v10.php
12   *
13   * All rights reserved.
14   *
15   * Redistribution and use in source and binary forms, with or
16   * without modification, are permitted provided that the following
17   * conditions are met:
18   *
19   * - Redistributions of source code must retain the above copyright
20   *   notice, this list of conditions and the following disclaimer.
21   *
22   * - Redistributions in binary form must reproduce the above
23   *   copyright notice, this list of conditions and the following
24   *   disclaimer in the documentation and/or other materials provided
25   *   with the distribution.
26   *
27   * - Neither the name of the Eclipse Foundation, Inc. nor the
28   *   names of its contributors may be used to endorse or promote
29   *   products derived from this software without specific prior
30   *   written permission.
31   *
32   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
33   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
34   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
35   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
36   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
37   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
38   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
39   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
40   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
41   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
42   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
43   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
44   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45   */
46  
47  package org.eclipse.jgit.util;
48  
49  import java.io.File;
50  import java.net.InetAddress;
51  import java.net.UnknownHostException;
52  import java.security.AccessController;
53  import java.security.PrivilegedAction;
54  import java.text.DateFormat;
55  import java.text.SimpleDateFormat;
56  import java.util.Locale;
57  import java.util.TimeZone;
58  
59  import org.eclipse.jgit.errors.CorruptObjectException;
60  import org.eclipse.jgit.lib.Config;
61  import org.eclipse.jgit.lib.ObjectChecker;
62  import org.eclipse.jgit.storage.file.FileBasedConfig;
63  import org.eclipse.jgit.util.time.MonotonicClock;
64  import org.eclipse.jgit.util.time.MonotonicSystemClock;
65  
66  /**
67   * Interface to read values from the system.
68   * <p>
69   * When writing unit tests, extending this interface with a custom class
70   * permits to simulate an access to a system variable or property and
71   * permits to control the user's global configuration.
72   * </p>
73   */
74  public abstract class SystemReader {
75  	private static final SystemReader DEFAULT;
76  
77  	private static Boolean isMacOS;
78  
79  	private static Boolean isWindows;
80  
81  	static {
82  		SystemReader r = new Default();
83  		r.init();
84  		DEFAULT = r;
85  	}
86  
87  	private static class Default extends SystemReader {
88  		private volatile String hostname;
89  
90  		@Override
91  		public String getenv(String variable) {
92  			return System.getenv(variable);
93  		}
94  
95  		@Override
96  		public String getProperty(String key) {
97  			return System.getProperty(key);
98  		}
99  
100 		@Override
101 		public FileBasedConfig openSystemConfig(Config parent, FS fs) {
102 			File configFile = fs.getGitSystemConfig();
103 			if (configFile == null) {
104 				return new FileBasedConfig(null, fs) {
105 					@Override
106 					public void load() {
107 						// empty, do not load
108 					}
109 
110 					@Override
111 					public boolean isOutdated() {
112 						// regular class would bomb here
113 						return false;
114 					}
115 				};
116 			}
117 			return new FileBasedConfig(parent, configFile, fs);
118 		}
119 
120 		@Override
121 		public FileBasedConfig openUserConfig(Config parent, FS fs) {
122 			final File home = fs.userHome();
123 			return new FileBasedConfig(parent, new File(home, ".gitconfig"), fs); //$NON-NLS-1$
124 		}
125 
126 		@Override
127 		public String getHostname() {
128 			if (hostname == null) {
129 				try {
130 					InetAddress localMachine = InetAddress.getLocalHost();
131 					hostname = localMachine.getCanonicalHostName();
132 				} catch (UnknownHostException e) {
133 					// we do nothing
134 					hostname = "localhost"; //$NON-NLS-1$
135 				}
136 				assert hostname != null;
137 			}
138 			return hostname;
139 		}
140 
141 		@Override
142 		public long getCurrentTime() {
143 			return System.currentTimeMillis();
144 		}
145 
146 		@Override
147 		public int getTimezone(long when) {
148 			return getTimeZone().getOffset(when) / (60 * 1000);
149 		}
150 	}
151 
152 	private static SystemReader INSTANCE = DEFAULT;
153 
154 	/**
155 	 * Get time since epoch, with up to millisecond resolution.
156 	 *
157 	 * @return time since epoch, with up to millisecond resolution.
158 	 */
159 	public static SystemReader getInstance() {
160 		return INSTANCE;
161 	}
162 
163 	/**
164 	 * Set the new instance to use when accessing properties.
165 	 *
166 	 * @param newReader
167 	 *            the new instance to use when accessing properties, or null for
168 	 *            the default instance.
169 	 */
170 	public static void setInstance(SystemReader newReader) {
171 		isMacOS = null;
172 		isWindows = null;
173 		if (newReader == null)
174 			INSTANCE = DEFAULT;
175 		else {
176 			newReader.init();
177 			INSTANCE = newReader;
178 		}
179 	}
180 
181 	private ObjectChecker platformChecker;
182 
183 	private void init() {
184 		// Creating ObjectChecker must be deferred. Unit tests change
185 		// behavior of is{Windows,MacOS} in constructor of subclass.
186 		if (platformChecker == null)
187 			setPlatformChecker();
188 	}
189 
190 	/**
191 	 * Should be used in tests when the platform is explicitly changed.
192 	 *
193 	 * @since 3.6
194 	 */
195 	protected final void setPlatformChecker() {
196 		platformChecker = new ObjectChecker()
197 			.setSafeForWindows(isWindows())
198 			.setSafeForMacOS(isMacOS());
199 	}
200 
201 	/**
202 	 * Gets the hostname of the local host. If no hostname can be found, the
203 	 * hostname is set to the default value "localhost".
204 	 *
205 	 * @return the canonical hostname
206 	 */
207 	public abstract String getHostname();
208 
209 	/**
210 	 * Get value of the system variable
211 	 *
212 	 * @param variable
213 	 *            system variable to read
214 	 * @return value of the system variable
215 	 */
216 	public abstract String getenv(String variable);
217 
218 	/**
219 	 * Get value of the system property
220 	 *
221 	 * @param key
222 	 *            of the system property to read
223 	 * @return value of the system property
224 	 */
225 	public abstract String getProperty(String key);
226 
227 	/**
228 	 * Open the git configuration found in the user home
229 	 *
230 	 * @param parent
231 	 *            a config with values not found directly in the returned config
232 	 * @param fs
233 	 *            the file system abstraction which will be necessary to perform
234 	 *            certain file system operations.
235 	 * @return the git configuration found in the user home
236 	 */
237 	public abstract FileBasedConfig openUserConfig(Config parent, FS fs);
238 
239 	/**
240 	 * Open the gitconfig configuration found in the system-wide "etc" directory
241 	 *
242 	 * @param parent
243 	 *            a config with values not found directly in the returned
244 	 *            config. Null is a reasonable value here.
245 	 * @param fs
246 	 *            the file system abstraction which will be necessary to perform
247 	 *            certain file system operations.
248 	 * @return the gitconfig configuration found in the system-wide "etc"
249 	 *         directory
250 	 */
251 	public abstract FileBasedConfig openSystemConfig(Config parent, FS fs);
252 
253 	/**
254 	 * Get the current system time
255 	 *
256 	 * @return the current system time
257 	 */
258 	public abstract long getCurrentTime();
259 
260 	/**
261 	 * Get clock instance preferred by this system.
262 	 *
263 	 * @return clock instance preferred by this system.
264 	 * @since 4.6
265 	 */
266 	public MonotonicClock getClock() {
267 		return new MonotonicSystemClock();
268 	}
269 
270 	/**
271 	 * Get the local time zone
272 	 *
273 	 * @param when
274 	 *            a system timestamp
275 	 * @return the local time zone
276 	 */
277 	public abstract int getTimezone(long when);
278 
279 	/**
280 	 * Get system time zone, possibly mocked for testing
281 	 *
282 	 * @return system time zone, possibly mocked for testing
283 	 * @since 1.2
284 	 */
285 	public TimeZone getTimeZone() {
286 		return TimeZone.getDefault();
287 	}
288 
289 	/**
290 	 * Get the locale to use
291 	 *
292 	 * @return the locale to use
293 	 * @since 1.2
294 	 */
295 	public Locale getLocale() {
296 		return Locale.getDefault();
297 	}
298 
299 	/**
300 	 * Returns a simple date format instance as specified by the given pattern.
301 	 *
302 	 * @param pattern
303 	 *            the pattern as defined in
304 	 *            {@link java.text.SimpleDateFormat#SimpleDateFormat(String)}
305 	 * @return the simple date format
306 	 * @since 2.0
307 	 */
308 	public SimpleDateFormat getSimpleDateFormat(String pattern) {
309 		return new SimpleDateFormat(pattern);
310 	}
311 
312 	/**
313 	 * Returns a simple date format instance as specified by the given pattern.
314 	 *
315 	 * @param pattern
316 	 *            the pattern as defined in
317 	 *            {@link java.text.SimpleDateFormat#SimpleDateFormat(String)}
318 	 * @param locale
319 	 *            locale to be used for the {@code SimpleDateFormat}
320 	 * @return the simple date format
321 	 * @since 3.2
322 	 */
323 	public SimpleDateFormat getSimpleDateFormat(String pattern, Locale locale) {
324 		return new SimpleDateFormat(pattern, locale);
325 	}
326 
327 	/**
328 	 * Returns a date/time format instance for the given styles.
329 	 *
330 	 * @param dateStyle
331 	 *            the date style as specified in
332 	 *            {@link java.text.DateFormat#getDateTimeInstance(int, int)}
333 	 * @param timeStyle
334 	 *            the time style as specified in
335 	 *            {@link java.text.DateFormat#getDateTimeInstance(int, int)}
336 	 * @return the date format
337 	 * @since 2.0
338 	 */
339 	public DateFormat getDateTimeInstance(int dateStyle, int timeStyle) {
340 		return DateFormat.getDateTimeInstance(dateStyle, timeStyle);
341 	}
342 
343 	/**
344 	 * Whether we are running on Windows.
345 	 *
346 	 * @return true if we are running on Windows.
347 	 */
348 	public boolean isWindows() {
349 		if (isWindows == null) {
350 			String osDotName = getOsName();
351 			isWindows = Boolean.valueOf(osDotName.startsWith("Windows")); //$NON-NLS-1$
352 		}
353 		return isWindows.booleanValue();
354 	}
355 
356 	/**
357 	 * Whether we are running on Mac OS X
358 	 *
359 	 * @return true if we are running on Mac OS X
360 	 */
361 	public boolean isMacOS() {
362 		if (isMacOS == null) {
363 			String osDotName = getOsName();
364 			isMacOS = Boolean.valueOf(
365 					"Mac OS X".equals(osDotName) || "Darwin".equals(osDotName)); //$NON-NLS-1$ //$NON-NLS-2$
366 		}
367 		return isMacOS.booleanValue();
368 	}
369 
370 	private String getOsName() {
371 		return AccessController.doPrivileged(new PrivilegedAction<String>() {
372 			@Override
373 			public String run() {
374 				return getProperty("os.name"); //$NON-NLS-1$
375 			}
376 		});
377 	}
378 
379 	/**
380 	 * Check tree path entry for validity.
381 	 * <p>
382 	 * Scans a multi-directory path string such as {@code "src/main.c"}.
383 	 *
384 	 * @param path path string to scan.
385 	 * @throws org.eclipse.jgit.errors.CorruptObjectException path is invalid.
386 	 * @since 3.6
387 	 */
388 	public void checkPath(String path) throws CorruptObjectException {
389 		platformChecker.checkPath(path);
390 	}
391 
392 	/**
393 	 * Check tree path entry for validity.
394 	 * <p>
395 	 * Scans a multi-directory path string such as {@code "src/main.c"}.
396 	 *
397 	 * @param path
398 	 *            path string to scan.
399 	 * @throws org.eclipse.jgit.errors.CorruptObjectException
400 	 *             path is invalid.
401 	 * @since 4.2
402 	 */
403 	public void checkPath(byte[] path) throws CorruptObjectException {
404 		platformChecker.checkPath(path, 0, path.length);
405 	}
406 }