/* * Jajuk * Copyright (C) The Jajuk Team * http://jajuk.info * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ package org.jajuk.util; import com.google.common.io.Files; import ext.MersenneTwister; import java.awt.Desktop; import java.awt.GraphicsEnvironment; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.net.InetAddress; import java.net.URL; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Random; import javax.swing.ImageIcon; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; import org.jajuk.Main; import org.jajuk.base.Device; import org.jajuk.base.DeviceManager; import org.jajuk.services.core.SessionService; import org.jajuk.util.error.JajukException; import org.jajuk.util.error.JajukRuntimeException; import org.jajuk.util.filters.DirectoryFilter; import org.jajuk.util.filters.KnownTypeFilter; import org.jajuk.util.log.Log; /** * Set of convenient methods for system and IO. */ public final class UtilSystem { /** The Constant LOCAL_IP. */ private static final String LOCAL_IP = "127.0.0.1"; /** Is browser supported ?. */ private static Boolean browserSupported; /** Size of the short names converter in bytes. */ private static final int CONVERTER_FILE_SIZE = 23; /** * MPlayer status possible values *. */ public static enum MPlayerStatus { MPLAYER_STATUS_OK, MPLAYER_STATUS_NOT_FOUND, MPLAYER_STATUS_WRONG_VERSION, MPLAYER_STATUS_JNLP_DOWNLOAD_PBM } /** Current date cached (for performances) *. */ public static final Date TODAY = new Date(); /** Central random object for all Jajuk *. */ private static final Random RANDOM = MersenneTwister.getInstance(); /** Cached user home directory *. */ private static String cachedUserHomeDir; /** Are we under Linux ? *. */ private static final boolean UNDER_LINUX; /** Are we under MAC OS Intel ? *. */ private static final boolean UNDER_OSX; /** Are we under Windows ? *. */ private static final boolean UNDER_WINDOWS; /** Are we under Windows 32 bits ? *. */ private static final boolean UNDER_WINDOWS_32BIT; /** Are we under Windows 64 bits ? *. */ private static final boolean UNDER_WINDOWS_64BIT; /** Are we in JNLP mode ? *. */ private static final boolean UNDER_JNLP; /** Are we under KDE ? *. */ private static final boolean UNDER_KDE; /** Directory filter used in refresh. */ private static JajukFileFilter dirFilter; /** File filter used in refresh. */ private static JajukFileFilter fileFilter; // Computes OS detection operations for perf reasons (can be called in loop // in refresh method for ie) static { final String sOS = (String) System.getProperties().get("os.name"); // os.name can be null with JWS under MacOS UNDER_WINDOWS = ((sOS != null) && (sOS.trim().toLowerCase(Locale.getDefault()) .lastIndexOf("windows") != -1)); } static { UNDER_WINDOWS_32BIT = UtilSystem.isUnderWindows() && System.getProperties().get("sun.arch.data.model").equals("32"); } static { UNDER_WINDOWS_64BIT = UtilSystem.isUnderWindows() && !System.getProperties().get("sun.arch.data.model").equals("32"); } static { final String sOS = (String) System.getProperties().get("os.name"); // os.name can be null with JWS under MacOS UNDER_LINUX = ((sOS != null) && (sOS.trim().toLowerCase(Locale.getDefault()) .lastIndexOf("linux") != -1)); } static { final String sArch = System.getProperty("os.arch"); UNDER_OSX = org.jdesktop.swingx.util.OS.isMacOSX() // We only support Intel OSX && ((sArch != null) && sArch.matches(".*86.*")); } static { UNDER_JNLP = (System.getProperty("jnlpx.jvm") != null); } /** * Are we running in a KDE environment ? * * We check it by using ps command + a grep searching 'ksmserver' process*/ static { boolean underKDE = false; if (isUnderLinux()) { BufferedReader stdInput = null; try { ProcessBuilder pb = new ProcessBuilder("ps", "-eaf"); Process proc = pb.start(); stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream())); proc.waitFor(); String s; while ((s = stdInput.readLine()) != null) { if (s.matches(".*ksmserver.*")) { underKDE = true; break; } } } catch (Throwable e) { Log.error(e); } finally { if (stdInput != null) { try { stdInput.close(); } catch (IOException e) { Log.error(e); } } } } UNDER_KDE = underKDE; } /** Icons cache. */ static Map<String, ImageIcon> iconCache = new HashMap<String, ImageIcon>(200); /** Mplayer exe path. */ private static File mplayerPath = null; /** current class loader. */ private static ClassLoader classLoader = null; /** * private constructor to avoid instantiating utility class. */ private UtilSystem() { } /** * Save a file in the same directory with name <filename>_YYYYmmddHHMM.xml and * with a given maximum Mb size for the file and its backup files * * @param file The file to back up * @param iMB */ public static void backupFile(final File file, final int iMB) { try { if (Integer.parseInt(Conf.getString(Const.CONF_BACKUP_SIZE)) <= 0) { // 0 or less means no backup return; } // calculates total size in MB for the file to backup and its // backup files long lUsedMB = 0; final List<File> alFiles = new ArrayList<File>(10); final File[] files = new File(file.getAbsolutePath()).getParentFile().listFiles(); if (files != null) { for (final File element : files) { if (element.getName().indexOf(UtilSystem.removeExtension(file.getName())) != -1) { lUsedMB += element.length(); alFiles.add(element); } } // sort found files alFiles.remove(file); Collections.sort(alFiles); // too much backup files, delete older if (((lUsedMB - file.length()) / 1048576 > iMB) && (alFiles.size() > 0)) { final File fileToDelete = alFiles.get(0); if (fileToDelete != null) { //NOSONAR if (!fileToDelete.delete()) { Log.warn("Could not delete file " + fileToDelete); } } } } // backup itself using nio, file name is // collection-backup-yyyMMdd.xml final String sExt = new SimpleDateFormat("yyyyMMdd", Locale.getDefault()).format(new Date()); final File fileNew = new File(UtilSystem.removeExtension(file.getAbsolutePath()) + "-backup-" + sExt + "." + UtilSystem.getExtension(file)); final FileChannel fcSrc = new FileInputStream(file).getChannel(); try { final FileChannel fcDest = new FileOutputStream(fileNew).getChannel(); try { fcDest.transferFrom(fcSrc, 0, fcSrc.size()); } finally { fcDest.close(); } } finally { fcSrc.close(); } } catch (final IOException ie) { Log.error(ie); } } /** * Copy a file to another file. * * @param file : file to copy * @param fNew : destination file * * @throws JajukException the jajuk exception * @throws IOException Signals that an I/O exception has occurred. */ public static void copy(final File file, final File fNew) throws JajukException, IOException { if (!file.exists() || !file.canRead()) { throw new JajukException(9, file.getAbsolutePath(), null); } FileUtils.copyFile(file, fNew); // Display a warning if copied file is void as it can happen with full // disks if (fNew.length() == 0) { Log.warn("Copied file is void: {{" + file.getAbsolutePath() + "}}"); } } /** * Return the device containing the io file * @param fio the IO file to test * @return the containing device or null if the file is outside any known device */ public static Device getDeviceForFio(java.io.File fio) { for (Device device : DeviceManager.getInstance().getDevices()) { if (UtilSystem.isAncestor(device.getFIO(), fio)) { return device; } } return null; } /** * Move a file to another file (directories are not supported). * * Note that it may be better to use this method than java.io.File.renameTo() method that * doesn't seem to work always under windows (in special directories) and because this method * always return an exception in case of problem. * * @param file : file to move * @param fNew : destination file * * @throws JajukException the jajuk exception * @throws IOException Signals that an I/O exception has occurred. */ public static void move(final File file, final File fNew) throws JajukException, IOException { copy(file, fNew); if (!file.delete()) { throw new IOException("Cannot delete file: " + file.getAbsolutePath()); } } /** * Copy a file. * * @param file : source file * @param sNewName : dest file * * @throws JajukException the jajuk exception * @throws IOException Signals that an I/O exception has occurred. */ public static void copy(final File file, final String sNewName) throws JajukException, IOException { Log.debug("Renaming: {{" + file.getAbsolutePath() + "}} to : " + sNewName); final File fileNew = new File(new StringBuilder(file.getParentFile().getAbsolutePath()) .append('/').append(sNewName).toString()); if (!file.exists() || !file.canRead()) { throw new JajukException(9, file.getAbsolutePath(), null); } FileUtils.copyFile(file, fileNew); } /** * Copy a URL resource to a file We don't use nio but Buffered Reader / writer * because we can only get channels from a FileInputStream that can be or not * be in a Jar (production / test). * * @param src source designed by URL * @param dest destination file full path * * @throws IOException If the src or dest cannot be opened/created. */ public static void copy(final URL src, final String dest) throws IOException { final BufferedReader br = new BufferedReader(new InputStreamReader(src.openStream())); try { final BufferedWriter bw = new BufferedWriter(new FileWriter(dest)); try { String sLine = null; do { sLine = br.readLine(); if (sLine != null) { bw.write(sLine); bw.newLine(); } } while (sLine != null); bw.flush(); } finally { bw.close(); } } finally { br.close(); } } /** * Activate recovery support for a file than has been written into the the collection so it can be gratefully recover * in case of breakdown during the save operation. This method must be called after the file.xml.sav has been successfully written. * <pre>The steps are the following (every step can fail, all files in the same (collection) directory, thus on the same disk) : * 1) Write file.xml.saving (already done when calling this method) * 2) Write the proof file file.xml.proof (void) * 3) Delete the file.xml file if it exists (optional under POSIX systems but we do it for consistency with Windows and to avoid a OS specific behavior) * 4) Delete the proof file * 5) Rename file.xml.saving to file.xml</pre> * </p> * @param finalFile : the final file (like collection.xml) * @throws IOException */ public static void saveFileWithRecoverySupport(File finalFile) throws IOException { // Check saving file existence File saving = new File(finalFile.getAbsoluteFile() + "." + Const.FILE_SAVING_FILE_EXTENSION); if (!saving.exists()) { throw new IOException("Saving file does not exist for file : " + finalFile.getAbsolutePath()); } // Create the proof file File proof = new File(finalFile.getAbsoluteFile() + "." + Const.FILE_SAVED_PROOF_FILE_EXTENSION); Files.touch(proof); if (finalFile.exists()) { deleteFile(finalFile); } deleteFile(proof); saving.renameTo(finalFile); } /** * Recover a file after a breakdown (at next jajuk session) if required. Most of the time, this does nothing but * if a file has been partially saved using the @see saveFileWithRecoverySupport() method, the previous version is revored. * This is guarantee to work always, except if the filesystem can't be read or written. * * Note that this generic method doesn't handle the special collection.xml backup files. * * <pre>Recovery actions and files existence when failure at step : * No failure : only file.xml file -> no recovery action * @1 : file.xml, partial file.xml.saving and no file.xml.proof -> delete file.xml.sav and file.xml.proof * @2 : file.xml, file.xml.saving but no file.xml.proof -> delete file.sav * @3 : file.xml, file.xml.saving and file.xml.proof -> execute saveFileWithRecoverySupport steps 3 to 5 * @4 : file.sav and file.xml.proof -> execute saveFileWithRecoverySupport steps 4 to 5 * @5 : file.xml.saving only -> execute saveFileWithRecoverySupport step 5</pre> * @throws IOException if a temporary file cannot be deleted */ public static void recoverFileIfRequired(File finalFile) throws IOException { File saving = new File(finalFile.getAbsoluteFile() + "." + Const.FILE_SAVING_FILE_EXTENSION); File proof = new File(finalFile.getAbsoluteFile() + "." + Const.FILE_SAVED_PROOF_FILE_EXTENSION); // No recovery required if (!saving.exists() && !proof.exists()) { return; } // Recovery after crash at saving step 1 and 2 if (finalFile.exists() && saving.exists() && !proof.exists()) { Log.warn("Recover step 1 or 2"); deleteFile(saving); } // Recovery after crash at saving step 3 else if (finalFile.exists() && saving.exists() && proof.exists()) { Log.warn("Recover step 3"); deleteFile(finalFile); deleteFile(proof); saving.renameTo(finalFile); } // Recovery after crash at saving step 4 else if (!finalFile.exists() && saving.exists() && proof.exists()) { Log.warn("Recover step 4"); deleteFile(proof); saving.renameTo(finalFile); } // Recovery after crash at saving step 5 else if (!finalFile.exists() && saving.exists() && !proof.exists()) { Log.warn("Recover step 5"); saving.renameTo(finalFile); } } /** * Copy recursively files and directories. * * @param src * @param dst * @throws JajukException the jajuk exception * @throws IOException Signals that an I/O exception has occurred. */ public static void copyRecursively(final File src, final File dst) throws JajukException, IOException { if (src.isDirectory()) { if (!dst.mkdirs()) { Log.warn("Could not create directory structure " + dst.toString()); } final String list[] = src.list(); for (final String element : list) { final String dest1 = dst.getAbsolutePath() + '/' + element; final String src1 = src.getAbsolutePath() + '/' + element; UtilSystem.copyRecursively(new File(src1), new File(dest1)); } } else { UtilSystem.copy(src, dst); } } /** * Copy a file to given directory. * * @param file : file to copy * @param directory : destination directory * * @return destination file * * @throws JajukException the jajuk exception * @throws IOException Signals that an I/O exception has occurred. */ public static void copyToDir(final File file, final File directory) throws JajukException, IOException { Log.debug("Copying: {{" + file.getAbsolutePath() + "}} to : " + directory.getAbsolutePath()); if (!file.exists() || !file.canRead()) { throw new JajukException(9, file.getAbsolutePath(), null); } FileUtils.copyFileToDirectory(file, directory); } /** * Create empty file. * * @param file * * @throws IOException Signals that an I/O exception has occurred. */ public static void createEmptyFile(final File file) throws IOException { final OutputStream fos = new FileOutputStream(file); try { fos.write(new byte[0]); } finally { fos.close(); } } /** * Delete recursively a directory (use with caution!). * * @param dir : source directory * * @throws IOException Signals that an I/O exception has occurred. */ public static void deleteDir(final File dir) throws IOException { Log.debug("Deleting: {{" + dir.getAbsolutePath() + "}}"); FileUtils.deleteDirectory(dir); if (dir.exists()) { throw new IOException("Directory" + dir.getAbsolutePath() + " still exists"); } return; } /** * Delete a file that exists. * * @param file : source file * * @throws IOException Signals that an I/O exception has occurred. */ public static void deleteFile(final File file) throws IOException { Log.debug("Deleting: {{" + file.getAbsolutePath() + "}}"); if (!file.isFile()) { throw new IOException("File " + file.getAbsolutePath() + " is not a file"); } if (!file.exists()) { throw new IOException("File " + file.getAbsolutePath() + " didn't exist"); } if (!file.delete()) { Log.warn("Could not delete file " + file); } // check that file has been really deleted (sometimes, // we get no exception) if (file.exists()) { throw new IOException("File" + file.getAbsolutePath() + " still exists"); } return; } /** * Get a file extension. * * @param file * * @return the extension */ public static String getExtension(final File file) { return UtilSystem.getExtension(file.getName()); } /** * Get a file extension (without the dot!). * * @param filename The file to examine. * * @return The actual file extension or an empty string if no extension is * found (i.e. no dot in the filename). */ public static String getExtension(final String filename) { int dotIndex = filename.lastIndexOf('.'); // File without point if (dotIndex == -1) { return ""; } if (dotIndex > 0) { return filename.substring(dotIndex + 1, filename.length()); } else { // File beginning by a point (unix hidden file) return filename; } } /** * Additional file checksum used to prevent bug #886098. Simply return some * bytes read at the middle of the file * <p> * uses nio api for performances * * @param fio * * @return the file checksum * * @throws JajukException the jajuk exception */ public static String getFileChecksum(final File fio) throws JajukException { try { String sOut = ""; final FileChannel fc = new FileInputStream(fio).getChannel(); try { final ByteBuffer bb = ByteBuffer.allocate(500); fc.read(bb, fio.length() / 2); sOut = new String(bb.array()); } finally { fc.close(); } return MD5Processor.hash(sOut); } catch (final IOException e) { throw new JajukException(103, e); } } /** * Gets the host name. * * @return This box hostname */ public static String getHostName() { String sHostname = null; // Try to get hostname using the standard way try { sHostname = InetAddress.getLocalHost().getHostName(); } catch (final Exception e) { Log.debug("Cannot get Hostname using the standard way"); } if (sHostname == null) { // Try using IP now try { final java.net.InetAddress inetAdd = java.net.InetAddress.getByName(LOCAL_IP); sHostname = inetAdd.getHostName(); } catch (final Exception e) { Log.debug("Cannot get Hostname by IP"); } } // If still no hostname, return a default value if (sHostname == null) { sHostname = Const.DEFAULT_HOSTNAME; } return sHostname; } /** * Return url of jar we are executing. * * This code no more work with last JRE 6 under JNLP (it returns only partial URL) * * @param cClass * * @return URL of jar we are executing */ public static URL getJarLocation(final Class<?> cClass) { URL url = cClass.getProtectionDomain().getCodeSource().getLocation(); if (UtilSystem.isUnderJNLP()) { Log.debug("JAR location: " + url.getFile()); } return url; } /** * Gets the m player windows path. * * @return MPlayer exe file */ public static File getMPlayerWindowsPath() { // Use cache if (UtilSystem.mplayerPath != null) { return UtilSystem.mplayerPath; } File file = null; // Check in ~/.jajuk directory (used by JNLP distribution // as well). Test exe size as well to detect unfinished downloads of // mplayer.exe in JNLP mode file = SessionService.getConfFileByPath(Const.FILE_MPLAYER_WINDOWS_EXE); if (file.exists() && file.length() == Const.MPLAYER_WINDOWS_EXE_SIZE) { UtilSystem.mplayerPath = file; return UtilSystem.mplayerPath; } else if (!UtilSystem.isUnderJNLP()) { // Check in the path where jajuk.jar is executed (all others // distributions). does not work under JNLP String sPATH = null; try { // Extract file name from URL. URI returns jar path, its parent // is the bin directory and the right dir is the parent of bin // dir sPATH = new File(getJarLocation(Main.class).toURI()).getParentFile().getParentFile() .getAbsolutePath(); // Add MPlayer file name file = new File(sPATH + '/' + Const.FILE_MPLAYER_WINDOWS_EXE); if (file.exists() && file.length() == Const.MPLAYER_WINDOWS_EXE_SIZE) { UtilSystem.mplayerPath = file; } } catch (Exception e) { Log.error(e); } } return UtilSystem.mplayerPath; // can be null if none suitable file found } /** * Gets the mplayer OSX path. * It is mainly based upon Windows getWindowsPath() path method, see comments over there * * @return MPLayer binary MAC full path */ public static File getMPlayerOSXPath() { if (UtilSystem.mplayerPath != null) { return UtilSystem.mplayerPath; } // Search in /Applications first File file = new File("/Applications/Jajuk.app/Contents/MacOS/" + Const.FILE_MPLAYER_OSX_EXE); if (file.canExecute() && file.length() == Const.MPLAYER_OSX_EXE_SIZE) { UtilSystem.mplayerPath = file; return UtilSystem.mplayerPath; } // Search in collection path file = SessionService.getConfFileByPath(Const.FILE_MPLAYER_OSX_EXE); if (file.exists() && file.length() == Const.MPLAYER_OSX_EXE_SIZE) { UtilSystem.mplayerPath = file; return UtilSystem.mplayerPath; } else if (!UtilSystem.isUnderJNLP()) { // Search in jajuk installation directory, do not work under JNLP String sPATH = null; try { if (SessionService.isIdeMode()) { // If under dev, take mplayer exe file from /Applications (the mplayer osx binary is not // in SCM) sPATH = "/Applications"; } else { sPATH = new File(getJarLocation(Main.class).toURI()).getParentFile().getParentFile() .getAbsolutePath(); } file = new File(sPATH + '/' + Const.FILE_MPLAYER_OSX_EXE); if (file.exists() && file.length() == Const.MPLAYER_OSX_EXE_SIZE) { UtilSystem.mplayerPath = file; } } catch (Exception e) { Log.error(e); } } return UtilSystem.mplayerPath; // can be null if none suitable file found } /** * Gets the mplayer status. * * @param mplayerPATH * * @return the mplayer status */ public static UtilSystem.MPlayerStatus getMplayerStatus(final String mplayerPATH) { Process proc = null; UtilSystem.MPlayerStatus mplayerStatus = UtilSystem.MPlayerStatus.MPLAYER_STATUS_NOT_FOUND; try { String fullPath = null; if ("".equals(mplayerPATH)) { fullPath = "mplayer"; } else { fullPath = mplayerPATH; } Log.debug("Testing path: " + fullPath); // check MPlayer release : 1.0pre8 min proc = Runtime.getRuntime().exec(new String[] { fullPath, "-input", "cmdlist" }); //$NON-NLS-2$ final BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream())); try { String line = null; mplayerStatus = UtilSystem.MPlayerStatus.MPLAYER_STATUS_WRONG_VERSION; for (;;) { line = in.readLine(); if (line == null) { break; } if (line.matches("get_time_pos.*")) { mplayerStatus = UtilSystem.MPlayerStatus.MPLAYER_STATUS_OK; break; } } } finally { in.close(); } } catch (final IOException e) { mplayerStatus = UtilSystem.MPlayerStatus.MPLAYER_STATUS_NOT_FOUND; } return mplayerStatus; } /** * This method intends to cleanup a future filename so it can be created on * all operating systems. Windows forbids characters : /\"<>|:*? * * @param in filename * * @return filename with forbidden characters replaced at best */ public static String getNormalizedFilename(final String in) { String out = in.trim(); // Replace / : < > and \ by - out = in.replaceAll("[/:<>\\\\]", "-"); // Replace * and | by spaces out = out.replaceAll("[\\*|]", " "); // Remove " and ? characters out = out.replaceAll("[\"\\?]", ""); return out; } /** * Return only the name of a file from a complete URL. * * @param sPath * * @return the only file */ public static String getOnlyFile(final String sPath) { return new File(sPath).getName(); } /** * Resource loading is done this way to meet the requirements for Web Start. * http * ://java.sun.com/j2se/1.5.0/docs/guide/javaws/developersguide/faq.html#211 * * @param name * * @return the resource */ public static URL getResource(final String name) { return UtilSystem.getClassLoader().getResource(name); } /** * Checks if is ancestor. * * @param file1 potential ancestor * @param file2 potential child * * @return whether file1 is a file2 ancestor */ public static boolean isAncestor(final File file1, final File file2) { File fParent = file2.getParentFile(); boolean bOut = false; while (fParent != null) { if (fParent.equals(file1)) { bOut = true; break; } fParent = fParent.getParentFile(); } return bOut; } /** * Checks if is descendant. * * @param file1 * @param file2 * * @return whether file1 is a file2 descendant */ public static boolean isDescendant(final File file1, final File file2) { // a file is a descendant to another file if the other file is it's // ancestor... return isAncestor(file2, file1); } /** * Checks if is under linux. * * @return whether we are under Linux */ public static boolean isUnderLinux() { return UtilSystem.UNDER_LINUX; } /** * Checks if we are running as jnlp app. * * @return whether we are running as jnlp app */ public static boolean isUnderJNLP() { return UtilSystem.UNDER_JNLP; } /** * Checks if is under OSX (Intel or PowerPC). * * @return whether we are under OS X */ public static boolean isUnderOSX() { return UtilSystem.UNDER_OSX; } /** * Checks if is under windows. * * @return whether we are under Windows */ public static boolean isUnderWindows() { return UtilSystem.UNDER_WINDOWS; } /** * Checks if is under windows32bits. * * @return whether we are under Windows 32 bits */ public static boolean isUnderWindows32bits() { return UtilSystem.UNDER_WINDOWS_32BIT; } /** * Checks if is under windows64bits. * * @return whether we are under Windows 64 bits */ public static boolean isUnderWindows64bits() { return UtilSystem.UNDER_WINDOWS_64BIT; } /** * Checks if is valid file name. * * @param parent parent directory * @param name file name * * @return whether the file name is correct on the current filesystem */ public static boolean isValidFileName(final File parent, final String name) { // General tests if ((parent == null) || (name == null)) { return false; } // only digits or letters, OK, no need to test if (!UtilString.containsNonDigitOrLetters(name)) { return true; } final File f = new File(parent, name); if (!f.exists()) { try { // try to create the file f.createNewFile(); // test if the file is seen into the directory final File[] files = parent.listFiles(); boolean b = false; for (final File element : files) { if (element.getName().equals(name)) { b = true; break; } } // remove test file if (f.exists()) { UtilSystem.deleteFile(f); } return b; } catch (final IOException ioe) { return false; } } else { // file already exists return true; } } /** * Need full fc. * * @return whether we need a full gc or not */ public static boolean needFullFC() { final float fTotal = Runtime.getRuntime().totalMemory(); final float fFree = Runtime.getRuntime().freeMemory(); final float fLevel = (fTotal - fFree) / fTotal; return fLevel >= Const.NEED_FULL_GC_LEVEL; } /** * Open a file and return a string buffer with the file content. * * @param path -File path * * @return StringBuilder - File content. * * @throws JajukException - Throws a JajukException if a problem occurs during the file * access. */ public static StringBuilder readFile(final String path) throws JajukException { // Read File file = new File(path); FileReader fileReader; try { fileReader = new FileReader(file); } catch (final FileNotFoundException e) { throw new JajukException(9, path, e); } try { final BufferedReader input = new BufferedReader(fileReader); try { // Read final StringBuilder strColl = new StringBuilder(); String line = null; while ((line = input.readLine()) != null) { strColl.append(line); } return strColl; } finally { // Close the bufferedReader input.close(); } } catch (final IOException e) { throw new JajukException(9, path, e); } } /** * Open a file from current jar and return a string buffer with the file * content. * * @param sURL * * @return StringBuilder - File content. * * @throws JajukException -Throws a JajukException if a problem occurs during the file * access. */ public static StringBuilder readJarFile(final String sURL) throws JajukException { // Read InputStream is; StringBuilder sb = null; try { is = Main.class.getResourceAsStream(sURL); try { // Read final byte[] b = new byte[200]; sb = new StringBuilder(); int i = 0; do { i = is.read(b, 0, b.length); sb.append(new String(b)); } while (i > 0); } finally { // Close the bufferedReader is.close(); } } catch (final IOException e) { throw new JajukException(9, e); } return sb; } /** * Remove an extension from a file name. * * @param sFilename * * @return filename without extension */ public static String removeExtension(final String sFilename) { return sFilename.substring(0, sFilename.lastIndexOf('.')); } /** * Gets the class loader. * * @return the class loader */ public static ClassLoader getClassLoader() { if (UtilSystem.classLoader == null) { UtilSystem.classLoader = Thread.currentThread().getContextClassLoader(); } return UtilSystem.classLoader; } /** * Gets the dir filter. * * @return the dir filter */ public static JajukFileFilter getDirFilter() { if (dirFilter == null) { dirFilter = new JajukFileFilter(DirectoryFilter.getInstance()); } return dirFilter; } /** * Gets the file filter. * * @return the file filter */ public static JajukFileFilter getFileFilter() { if (fileFilter == null) { fileFilter = new JajukFileFilter(KnownTypeFilter.getInstance()); } return fileFilter; } /** * Replace a string inside a given file. * * @param file the file * @param oldS the string to replace * @param newS the new string * @param encoding the encoding of the file * * @return whether some replacements occurred */ public static boolean replaceInFile(File file, String oldS, String newS, String encoding) { try { String s = FileUtils.readFileToString(file); if (s.indexOf(oldS) != -1) { s = s.replaceAll(oldS, newS); Writer bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), encoding)); try { bw.write(s); bw.flush(); } finally { bw.close(); } return true; } } catch (IOException e) { Log.error(e); } return false; } /** * This method returns a single random object that can be used anywhere in * jajuk It has to be a singleton to get a good shuffling. Indeed, Random() * object are seeded by default with current nano date but in some cases, two * random could be created at the same exact date in different threads or the * same. * * @return Jajuk singleton random object */ public static Random getRandom() { return UtilSystem.RANDOM; } /** * Opens a directory with the associated explorer program. <li>Start by trying * to open the directory with any provided explorer path</li> <li>Then, try to * use the JDIC Desktop class if supported by the platform</li> * * Inspired from an aTunes method * * @param directory */ public static void openInExplorer(File directory) { final File directoryToOpen; /* * Needed for UNC filenames with spaces -> http://bugs.sun.com/view_bug.do?bug_id=6550588 */ if (isUnderWindows()) { String shortPath = getShortPathNameW(directory.getAbsolutePath()); // If shortnames conversion doesn't work, try the initial path if (shortPath == null || !new File(shortPath).exists()) { directoryToOpen = directory; } else { directoryToOpen = new File(shortPath); } } else { directoryToOpen = directory; } // Try to open the location using the forced explorer path of provided if (StringUtils.isNotBlank(Conf.getString(Const.CONF_EXPLORER_PATH))) { new Thread("Explorer Open Thread 1") { @Override public void run() { try { ProcessBuilder pb = new ProcessBuilder(Conf.getString(Const.CONF_EXPLORER_PATH), directoryToOpen.getAbsolutePath()); pb.start(); } catch (Exception e) { Log.error(e); Messages.showErrorMessage(179, directoryToOpen.getAbsolutePath()); } } }.start(); } // Try to open the location using the JDIC/JDK Desktop.open method // This is not supported on some platforms (Linux/XFCE for ie) else if (Desktop.getDesktop().isSupported(Desktop.Action.OPEN)) { new Thread("Explorer Open Thread 2") { @Override public void run() { try { Desktop.getDesktop().open(directoryToOpen); } catch (Exception e) { Log.error(e); Messages.showErrorMessage(179, directoryToOpen.getAbsolutePath()); } } }.start(); } // Else, display a warning message: we don't support this platform else { Messages.showErrorMessage(179); } } /** * Return whether a process is still running. * * @param process the process * * @return whether the process is still running */ public static boolean isRunning(Process process) { try { process.exitValue(); return false; } catch (IllegalThreadStateException itse) { return true; } } /** * Return a process exit value, -100 if the process is still running. * * @param process the process * * @return the process exit value, -100 if the process is still running */ public static int getExitValue(Process process) { try { return process.exitValue(); } catch (IllegalThreadStateException itse) { return -100; } } /** * Returns current user home directory handling Windows issues on JRE. * * @return current user home directory */ public static String getUserHome() { // User home is cached for performances if (cachedUserHomeDir != null) { return cachedUserHomeDir; } /** * We search first in USERPROFILE env directory before than user.home. * * But we give priority to user.home if it already contains a suitable jajuk * collection to maintain backward compatibility * * See #1473 and * http://bugs.sun.com/view_bug.do?bug_id=4787931 **/ if (StringUtils.isNotBlank(System.getenv("USERPROFILE"))) { cachedUserHomeDir = System.getenv("USERPROFILE"); } else { cachedUserHomeDir = System.getProperty("user.home"); } return cachedUserHomeDir; } /** * Convert a full regular Windows path to 8.3 DOS format * * @param longname the regular absolute path * * @return the shortname absolute path */ public static String getShortPathNameW(String longname) { // Find the shortname .bat converter, create it if it doesn't yet exist String shortname = null; try { File fileConverter = SessionService.getConfFileByPath(Const.FILE_FILENAME_CONVERTER); if (!fileConverter.exists() // Test that the converter version has not been updated // IMPORTANT ! Don't forget to update the CONVERTER_FILE_SIZE constant if you change the // script ! || (fileConverter.exists() && fileConverter.length() != CONVERTER_FILE_SIZE)) { FileWriter fw = new FileWriter(fileConverter); fw.write("@echo off\n"); fw.write("dir /x \"%~s1\""); fw.flush(); fw.close(); } // these two quotes are required in the case where both directory and file are non-ascii ProcessBuilder pc = new ProcessBuilder(fileConverter.getAbsolutePath(), "\"" + longname + "\""); Process process = pc.start(); /* * dir /x parsing : Sample output (in French but should work with any language): * * Le volume dans le lecteur D s'appelle Données * * Le numéro de série du volume est C880-0321 * * Répertoire de D:\MESDOC~1\MAMUSI~1\FILES_~1\1F19~1 * * 07/06/2010 21:49 <REP> . * * 07/06/2010 21:49 <REP> .. * * 07/06/2010 14:41 20 108 -(_)~1.MP3 µ×ùÕ│» - µÿÄÕñ®õ╗ÑÕ¥î (µ×ùÕ│»+µ│ÕàÆÕÉêÕö▒).mp3 */ BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream())); String line = ""; String lineDirectory = null; while ((line = br.readLine()) != null) { String ext = UtilSystem.getExtension(new File(longname)); if (StringUtils.isNotBlank(line)) { // Index of the file extension in short name int indexExtension = line.indexOf(longname.substring(0, 3).toUpperCase()); if (line.endsWith(ext)) { int indexEnd = line.indexOf(ext.toUpperCase()); int indexBegin = indexEnd; // Find the previous space while (line.charAt(indexBegin) != ' ') { indexBegin--; } shortname = line.substring(indexBegin, indexEnd + 4).trim(); break; } else if (indexExtension != -1) { // We get parent directory full path in shortname thanks the %~s1 in the script lineDirectory = line.substring(indexExtension, line.length()).trim(); } } } shortname = lineDirectory + "\\" + shortname; process.destroy(); } catch (Exception e) { throw new JajukRuntimeException("Cannot convert the filename to 8.3 format", e); } return shortname; } /** * Return whether the Desktop BROWSE action is actually supported * Note this bug : http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6486393 * under KDE : isSupported returns true but we get an exception when actually browsing * * @return true, if checks if is browser supported */ public static boolean isBrowserSupported() { // value stored for perf if (browserSupported != null) { return browserSupported; } // In server UT mode, just return false if (GraphicsEnvironment.isHeadless()) { return false; } else { if (Desktop.isDesktopSupported()) { // If under Linux, check if we're under KDE because of a sun bug, // see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6486393 and #1612 if (isUnderLinux() && isUnderKDE()) { return false; } else { return (Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)); } } else { return false; } } } /** * Are we running in a KDE environment ? * * We check it by using ps command + a grep searching 'ksmserver' process. * * @return whether we are running in a KDE environment */ public static boolean isUnderKDE() { return UtilSystem.UNDER_KDE; } }