| 1 | /** |
| 2 | * |
| 3 | */ |
| 4 | package atg.test.util; |
| 5 | |
| 6 | import java.io.BufferedReader; |
| 7 | import java.io.BufferedWriter; |
| 8 | import java.io.File; |
| 9 | import java.io.FileInputStream; |
| 10 | import java.io.FileOutputStream; |
| 11 | import java.io.FileReader; |
| 12 | import java.io.FileWriter; |
| 13 | import java.io.IOException; |
| 14 | import java.io.ObjectInputStream; |
| 15 | import java.io.ObjectOutput; |
| 16 | import java.io.ObjectOutputStream; |
| 17 | import java.nio.channels.FileChannel; |
| 18 | import java.util.HashMap; |
| 19 | import java.util.Iterator; |
| 20 | import java.util.List; |
| 21 | import java.util.Map; |
| 22 | import java.util.Map.Entry; |
| 23 | |
| 24 | import org.apache.log4j.Logger; |
| 25 | |
| 26 | import atg.applauncher.core.util.JarUtils; |
| 27 | |
| 28 | |
| 29 | |
| 30 | /** |
| 31 | * A collection of utility methods for dealing with the filesystem. |
| 32 | * @author robert |
| 33 | * |
| 34 | */ |
| 35 | @SuppressWarnings("unchecked") |
| 36 | public class FileUtil { |
| 37 | |
| 38 | private static Logger log = Logger.getLogger(FileUtil.class); |
| 39 | |
| 40 | private static boolean isDirty = false;; |
| 41 | |
| 42 | private static Map<String, Long> CONFIG_FILES_TIMESTAMPS, |
| 43 | CONFIG_FILES_GLOBAL_FORCE; |
| 44 | |
| 45 | /** |
| 46 | * |
| 47 | * @param srcDir |
| 48 | * @param dstDir |
| 49 | * @param excludes |
| 50 | * @throws IOException |
| 51 | */ |
| 52 | public static void copyDirectory(String srcDir, String dstDir, |
| 53 | final List<String> excludes) throws IOException { |
| 54 | if (new File(srcDir).exists()) { |
| 55 | new File(dstDir).mkdirs(); |
| 56 | for (final String file : new File(srcDir).list()) { |
| 57 | final String source = srcDir + File.separator + file, destination = dstDir |
| 58 | + File.separator + file; |
| 59 | boolean dir = new File(source).isDirectory(); |
| 60 | if (dir && !excludes.contains(file)) { |
| 61 | copyDirectory(source, destination, excludes); |
| 62 | } |
| 63 | else { |
| 64 | if (!excludes.contains(file)) { |
| 65 | copyFile(source, destination); |
| 66 | } |
| 67 | } |
| 68 | } |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | /** |
| 73 | * |
| 74 | * @param src |
| 75 | * @param dst |
| 76 | * @throws IOException |
| 77 | */ |
| 78 | public static void copyFile(final String src, final String dst) |
| 79 | throws IOException { |
| 80 | final File srcFile = new File(src); |
| 81 | final File dstFile = new File(dst); |
| 82 | |
| 83 | if (CONFIG_FILES_TIMESTAMPS != null |
| 84 | && CONFIG_FILES_TIMESTAMPS.get(src) != null |
| 85 | && CONFIG_FILES_TIMESTAMPS.get(src) == srcFile.lastModified() |
| 86 | && dstFile.exists()) { |
| 87 | if (log.isDebugEnabled()) { |
| 88 | log.debug(String |
| 89 | .format( |
| 90 | "%s last modified hasn't changed and destination still exists", |
| 91 | src)); |
| 92 | } |
| 93 | } |
| 94 | else { |
| 95 | if (log.isDebugEnabled()) { |
| 96 | log.debug(String.format("Copy: src file %s ts %s : ", src, srcFile |
| 97 | .lastModified())); |
| 98 | log.debug(String.format("Copy: dest file %s ts %s : ", dst, dstFile |
| 99 | .lastModified())); |
| 100 | } |
| 101 | |
| 102 | final FileChannel srcChannel = new FileInputStream(src).getChannel(); |
| 103 | final FileChannel dstChannel = new FileOutputStream(dst).getChannel(); |
| 104 | dstChannel.transferFrom(srcChannel, 0, srcChannel.size()); |
| 105 | dstChannel.close(); |
| 106 | srcChannel.close(); |
| 107 | } |
| 108 | |
| 109 | } |
| 110 | |
| 111 | /** |
| 112 | * |
| 113 | * @param componentName |
| 114 | * The name of the nucleus component |
| 115 | * @param configurationStagingLocation |
| 116 | * A valid not <code>null</code> directory. |
| 117 | * @param clazz |
| 118 | * The class implementing the nucleus component |
| 119 | * @param settings |
| 120 | * An implementation of {@link Map} containing all needed properties |
| 121 | * the component is depended on (eg key = username, value = test). |
| 122 | * Can be <code>null</code> or empty. |
| 123 | * @throws IOException |
| 124 | */ |
| 125 | public static void createPropertyFile(final String componentName, |
| 126 | final File configurationStagingLocation, final Class<?> clazz, |
| 127 | final Map<String, String> settings) throws IOException { |
| 128 | |
| 129 | configurationStagingLocation.mkdirs(); |
| 130 | final File propertyFile = new File(configurationStagingLocation, |
| 131 | componentName.replace("/", File.separator) + ".properties"); |
| 132 | new File(propertyFile.getParent()).mkdirs(); |
| 133 | propertyFile.createNewFile(); |
| 134 | final BufferedWriter out = new BufferedWriter(new FileWriter(propertyFile)); |
| 135 | |
| 136 | try { |
| 137 | if (clazz != null) { |
| 138 | out.write("$class=" + clazz.getName()); |
| 139 | out.newLine(); |
| 140 | } |
| 141 | if (settings != null) { |
| 142 | for (final Iterator<Entry<String, String>> it = settings.entrySet() |
| 143 | .iterator(); it.hasNext();) { |
| 144 | final Entry<String, String> entry = it.next(); |
| 145 | out.write(new StringBuilder(entry.getKey()).append("=").append( |
| 146 | entry.getValue()).toString()); |
| 147 | out.newLine(); |
| 148 | } |
| 149 | } |
| 150 | } |
| 151 | finally { |
| 152 | out.close(); |
| 153 | } |
| 154 | } |
| 155 | |
| 156 | public static final String COULD_NOT_DELETE_TEMP_DIRECTORY = "Couldn't delete temp directory. "; |
| 157 | |
| 158 | public void searchAndReplace(final String originalValue, |
| 159 | final String newValue, final File file) throws IOException { |
| 160 | |
| 161 | final File TMP_FILE = new File(System |
| 162 | .getProperty("java.io.tmpdir") |
| 163 | + File.separator + System.currentTimeMillis()+this+"-atg-dust-rh.tmp"); |
| 164 | TMP_FILE.deleteOnExit(); |
| 165 | |
| 166 | if (CONFIG_FILES_GLOBAL_FORCE != null |
| 167 | && CONFIG_FILES_GLOBAL_FORCE.get(file.getPath()) != null |
| 168 | && CONFIG_FILES_GLOBAL_FORCE.get(file.getPath()) == file.lastModified() |
| 169 | && file.exists()) { |
| 170 | isDirty = false; |
| 171 | if (log.isDebugEnabled()) { |
| 172 | log.debug(String.format( |
| 173 | "%s last modified hasn't changed and file still exists, no need for global scope force", |
| 174 | file.getPath())); |
| 175 | } |
| 176 | } |
| 177 | else { |
| 178 | final BufferedWriter out = new BufferedWriter(new FileWriter(TMP_FILE)); |
| 179 | final BufferedReader in = new BufferedReader(new FileReader(file)); |
| 180 | String str; |
| 181 | while ((str = in.readLine()) != null) { |
| 182 | if (str.contains(originalValue)) { |
| 183 | out.write(newValue); |
| 184 | } |
| 185 | else { |
| 186 | out.write(str); |
| 187 | out.newLine(); |
| 188 | } |
| 189 | } |
| 190 | out.close(); |
| 191 | in.close(); |
| 192 | JarUtils.copy(TMP_FILE, file, true, false); |
| 193 | CONFIG_FILES_GLOBAL_FORCE.put(file.getPath(), file.lastModified()); |
| 194 | isDirty = true; |
| 195 | } |
| 196 | |
| 197 | } |
| 198 | |
| 199 | /** |
| 200 | * Deletes the given directory when the JVM exits. |
| 201 | * This method does so by implementing a shutdown hook. |
| 202 | * @param tmpDir |
| 203 | */ |
| 204 | public static void deleteDirectoryOnShutdown(final File tmpDir) { |
| 205 | Runtime.getRuntime().addShutdownHook(new Thread() { |
| 206 | public void run() { |
| 207 | try { |
| 208 | atg.core.io.FileUtils.deleteDir(tmpDir.getAbsolutePath()); |
| 209 | } catch (IOException e) { |
| 210 | log.error(FileUtil.COULD_NOT_DELETE_TEMP_DIRECTORY, e); |
| 211 | } |
| 212 | } |
| 213 | }); |
| 214 | } |
| 215 | |
| 216 | public static void serialize(final File file, final Object o) |
| 217 | throws IOException { |
| 218 | |
| 219 | ObjectOutput out = null; |
| 220 | try { |
| 221 | out = new ObjectOutputStream(new FileOutputStream(file)); |
| 222 | out.writeObject(o); |
| 223 | } |
| 224 | finally { |
| 225 | if (out != null) { |
| 226 | out.close(); |
| 227 | } |
| 228 | } |
| 229 | } |
| 230 | |
| 231 | public static Map<String, Long> deserialize(final File file, |
| 232 | final long serialTtl) { |
| 233 | |
| 234 | if (file.exists() |
| 235 | && file.lastModified() < System.currentTimeMillis() - serialTtl) { |
| 236 | if (log.isDebugEnabled()) { |
| 237 | log.debug(String.format("Deleting previous serial %s " |
| 238 | + "because it's older then %s m/s", file.getPath(), serialTtl)); |
| 239 | } |
| 240 | file.delete(); |
| 241 | } |
| 242 | |
| 243 | Map<String, Long> o = null; |
| 244 | try { |
| 245 | if (file.exists()) { |
| 246 | final ObjectInputStream in = new ObjectInputStream(new FileInputStream( |
| 247 | file)); |
| 248 | try { |
| 249 | o = (Map<String, Long>) in.readObject(); |
| 250 | } |
| 251 | finally { |
| 252 | if (in != null) { |
| 253 | in.close(); |
| 254 | } |
| 255 | } |
| 256 | } |
| 257 | } |
| 258 | catch (Exception e) { |
| 259 | log.error("Error: ", e); |
| 260 | } |
| 261 | if (o == null) { |
| 262 | o = new HashMap<String, Long>(); |
| 263 | } |
| 264 | return o; |
| 265 | } |
| 266 | |
| 267 | public static void setConfigFilesTimestamps( |
| 268 | Map<String, Long> config_files_timestamps) { |
| 269 | CONFIG_FILES_TIMESTAMPS = config_files_timestamps; |
| 270 | } |
| 271 | |
| 272 | public static void setConfigFilesGlobalForce( |
| 273 | Map<String, Long> config_files_global_force) { |
| 274 | CONFIG_FILES_GLOBAL_FORCE = config_files_global_force; |
| 275 | } |
| 276 | |
| 277 | public static boolean isDirty() { |
| 278 | return isDirty; |
| 279 | } |
| 280 | |
| 281 | public static Map<String, Long> getConfigFilesTimestamps() { |
| 282 | return CONFIG_FILES_GLOBAL_FORCE; |
| 283 | } |
| 284 | |
| 285 | } |