001/* 002 * Copyright 2012-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.boot.system; 018 019import java.io.File; 020import java.security.MessageDigest; 021 022import org.springframework.util.Assert; 023import org.springframework.util.StringUtils; 024 025/** 026 * Provides access to an application specific temporary directory. Generally speaking 027 * different Spring Boot applications will get different locations, however, simply 028 * restarting an application will give the same location. 029 * 030 * @author Phillip Webb 031 * @since 2.0.0 032 */ 033public class ApplicationTemp { 034 035 private static final char[] HEX_CHARS = "0123456789ABCDEF".toCharArray(); 036 037 private final Class<?> sourceClass; 038 039 private volatile File dir; 040 041 /** 042 * Create a new {@link ApplicationTemp} instance. 043 */ 044 public ApplicationTemp() { 045 this(null); 046 } 047 048 /** 049 * Create a new {@link ApplicationTemp} instance for the specified source class. 050 * @param sourceClass the source class or {@code null} 051 */ 052 public ApplicationTemp(Class<?> sourceClass) { 053 this.sourceClass = sourceClass; 054 } 055 056 @Override 057 public String toString() { 058 return getDir().getAbsolutePath(); 059 } 060 061 /** 062 * Return a sub-directory of the application temp. 063 * @param subDir the sub-directory name 064 * @return a sub-directory 065 */ 066 public File getDir(String subDir) { 067 File dir = new File(getDir(), subDir); 068 dir.mkdirs(); 069 return dir; 070 } 071 072 /** 073 * Return the directory to be used for application specific temp files. 074 * @return the application temp directory 075 */ 076 public File getDir() { 077 if (this.dir == null) { 078 synchronized (this) { 079 byte[] hash = generateHash(this.sourceClass); 080 this.dir = new File(getTempDirectory(), toHexString(hash)); 081 this.dir.mkdirs(); 082 Assert.state(this.dir.exists(), 083 () -> "Unable to create temp directory " + this.dir); 084 } 085 } 086 return this.dir; 087 } 088 089 private File getTempDirectory() { 090 String property = System.getProperty("java.io.tmpdir"); 091 Assert.state(StringUtils.hasLength(property), "No 'java.io.tmpdir' property set"); 092 File file = new File(property); 093 Assert.state(file.exists(), () -> "Temp directory " + file + " does not exist"); 094 Assert.state(file.isDirectory(), 095 () -> "Temp location " + file + " is not a directory"); 096 return file; 097 } 098 099 private byte[] generateHash(Class<?> sourceClass) { 100 ApplicationHome home = new ApplicationHome(sourceClass); 101 MessageDigest digest; 102 try { 103 digest = MessageDigest.getInstance("SHA-1"); 104 update(digest, home.getSource()); 105 update(digest, home.getDir()); 106 update(digest, System.getProperty("user.dir")); 107 update(digest, System.getProperty("java.home")); 108 update(digest, System.getProperty("java.class.path")); 109 update(digest, System.getProperty("sun.java.command")); 110 update(digest, System.getProperty("sun.boot.class.path")); 111 return digest.digest(); 112 } 113 catch (Exception ex) { 114 throw new IllegalStateException(ex); 115 } 116 } 117 118 private void update(MessageDigest digest, Object source) { 119 if (source != null) { 120 digest.update(getUpdateSourceBytes(source)); 121 } 122 } 123 124 private byte[] getUpdateSourceBytes(Object source) { 125 if (source instanceof File) { 126 return getUpdateSourceBytes(((File) source).getAbsolutePath()); 127 } 128 return source.toString().getBytes(); 129 } 130 131 private String toHexString(byte[] bytes) { 132 char[] hex = new char[bytes.length * 2]; 133 for (int i = 0; i < bytes.length; i++) { 134 int b = bytes[i] & 0xFF; 135 hex[i * 2] = HEX_CHARS[b >>> 4]; 136 hex[i * 2 + 1] = HEX_CHARS[b & 0x0F]; 137 } 138 return new String(hex); 139 } 140 141}