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 }