package com.idega.core.ldap.client.cbutil; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.Arrays; import java.util.Comparator; import javax.naming.NamingException; import javax.naming.directory.Attribute; import javax.naming.directory.Attributes; /** * This class creates a cache directory called 'temp' where temporary files are stored. * Currently it works for audio, jpegPhoto and odDocumentDOC attributes. The audio * are stored in a sub dir called 'audio'. It also contains methods * for decreasing the cache size, cleaning (or emptying the cache and sorting the * temporary files according to their last modified date. * @author Trudi. */ public class CBCache { private static int counter = 0; //TE: a counter that is used to but a unique number on the temp files. private static int CACHEMAX = 100; //TE: the maximum size the cache should ever reach before decreasing its size. private static int CACHEMIN = 50; //TE: the size the cache gets minimized to when decreaseing its size. private static File fileDir = null; //TE: the directory that the temp files are stored. private static File audioFileDir = null; //TE: the directory that the audio temp files are stored. private static String allFiles[]; //TE: an array to store the files within the temp directory. private static String allAudioFiles[]; //TE: an array to store the files within the audio temp directory. private static String extension = ".jpg"; //TE: the extension of the temporary file. private static String dirPath = "temp"; /** * Creates a cache directory named 'temp' and adds temporary files to it currently naming them (for example) * with the hash of their 'DN + a unique number + .jpg'. * @param currentDN the dn of the entry being modified (will be used as part of the name of the temp file i.e: cn=Al,o=DEMOCORP,c=AU). * @param entry the actual entry that is being displayed. * @param type the attribute type for example: audio or jpegPhoto. * @param size the size of the number of specific attribute values this entry contains (i.e. 3 jpegPhoto attributes). * @author Trudi. */ public static void createCache(String currentDN, Attributes entry, String type, int size) { if (type.equalsIgnoreCase("audio")) { createAudioCache(currentDN, entry, type, size); return; } fileDir = makeDir(); allFiles = fileDir.list(); currentDN = Integer.toString(currentDN.hashCode()); for(int i=0; i<allFiles.length; i++) //TE: don't create temporary files if they already exist for the entry. { if (allFiles[i].startsWith(currentDN) && type.equalsIgnoreCase("jpegPhoto") && allFiles[i].endsWith(".jpg")) { return; } else if (allFiles[i].startsWith(currentDN) && type.equalsIgnoreCase("odDocumentDOC") && allFiles[i].endsWith(".doc")) { return; } else if (allFiles[i].startsWith(currentDN) && type.equalsIgnoreCase("odSpreadSheetXLS") && allFiles[i].endsWith(".xls")) { return; } else if (allFiles[i].startsWith(currentDN) && type.equalsIgnoreCase("odMovieAVI") && allFiles[i].endsWith(".avi")) { return; } else if (allFiles[i].startsWith(currentDN) && type.equalsIgnoreCase("odSoundWAV") && allFiles[i].endsWith(".wav")) { return; } else if (allFiles[i].startsWith(currentDN) && type.equalsIgnoreCase("odMusicMID") && allFiles[i].endsWith(".mid")) { return; } } for(int i=0; i<size; i++) { byte [] bytes = getAttByteValue(type, entry, i); //TE: gets the byte[] of the jpegPhoto/audio attribute. if(type.equalsIgnoreCase("odDocumentDOC")) { doNormalCache(currentDN, bytes, ".doc"); } else if(type.equalsIgnoreCase("odSpreadSheetXLS")) { doNormalCache(currentDN, bytes, ".xls"); } else if(type.equalsIgnoreCase("odMovieAVI")) { doNormalCache(currentDN, bytes, ".avi"); } else if(type.equalsIgnoreCase("odMusicMID")) { doNormalCache(currentDN, bytes, ".mid"); } else if(type.equalsIgnoreCase("odSoundWAV")) { doNormalCache(currentDN, bytes, ".wav"); } else if(type.equalsIgnoreCase("jpegPhoto")) { doNormalCache(currentDN, bytes, ".jpg"); } } if(allFiles.length > CACHEMAX) { decreaseCacheSize(); } } /** * Creates an audio cache directory named 'temp/audio' and adds temporary files to it currently naming * them (for example) with the hash of their 'DN + a unique number + .wav'. * @param currentDN the dn of the entry being modified (will be used as part of the name of the temp file i.e: cn=Al,o=DEMOCORP,c=AU). * @param entry the actual entry that is being displayed. * @param type the attribute type for example: audio or jpegPhoto. * @param size the size of the number of specific attribute values this entry contains (i.e. 3 audio attributes). * @author Trudi. */ public static void createAudioCache(String currentDN, Attributes entry, String type, int size) { if (!type.equalsIgnoreCase("audio")) { CBUtility.log("Error - trying to create a audio temporary cache with incorrect data in 'CBCache.createAudioCache'."); return; } audioFileDir = makeAudioDir(); allAudioFiles = audioFileDir.list(); currentDN = Integer.toString(currentDN.hashCode()); for(int i=0; i<allAudioFiles.length; i++) //TE: don't create temporary files if they already exist for the entry. { if (allAudioFiles[i].startsWith(currentDN)) { return; } } for(int i=0; i<size; i++) { byte [] bytes = getAttByteValue(type, entry, i); //TE: gets the byte[] of the jpegPhoto/audio attribute. if(type.equalsIgnoreCase("audio")) { doAudioCache(currentDN, bytes); } } if(allAudioFiles.length > CACHEMAX) { decreaseAudioCacheSize(); } } /** * Does the actual writing of the cache files. * @param currentDN the dn of the entry being modified (will be used as part of the name of the temp file i.e: cn=Al,o=DEMOCORP,c=AU). * @param bytes the byte[] of the audio attribute. * @param extension the extension of the cache file (currently .doc, .jpg). * @author Trudi. */ public static void doNormalCache(String currentDN, byte [] bytes, String extension) { String name = currentDN + counter + extension; File file = new File(fileDir, name); counter++; try { file.deleteOnExit(); //TE: deletes the temporary files when JX is shut down. FileOutputStream output = new FileOutputStream(file); output.write(bytes); output.close(); } catch (Exception e) { CBUtility.error(CBIntText.get("Problem writing the temporary file: ") + file.toString() + "\n" + e); } } /** * Creates the audio temporary files. First, the type of audio file needs to be determined. * This is done by converting the first 100 bytes of the audio file to hex then searching it for an * identifier of the audio file (for example in a .wav file the header should contain the word 'WAVE' * which in hex is '574156'). If the audio file type is determined the temporary file is created with * the current DN + a unique number + the extension for example: * <p> * cn=Al,o=DEMOCORP,c=AU9.wav * <p> * This method can determine the following audio file types: * <p> * .wav * .mp3 * .rmi * .ram * .aiff * .mid * .au * .stm * .voc * .xm * .s3m * .it * <p> * if an audio file type is unknown the temporary file is still created but with no extension. * @param currentDN the dn of the entry being modified (will be used as part of the name of the temp file i.e: cn=Al,o=DEMOCORP,c=AU). * @param bytes the byte[] of the audio attribute. * @author Trudi. */ public static void doAudioCache(String currentDN, byte [] bytes) { byte[] testBytes = new byte[100]; if (bytes.length > 100) { System.arraycopy(bytes, 0, testBytes, 0, 100); } else { testBytes = bytes; } String hexSub = CBUtility.bytes2Hex(testBytes); //TE: look at the header of the sound files and determine the extension. if(hexSub.indexOf("574156") > -1) { extension = ".wav"; } else if(hexSub.indexOf("494433") > -1) { extension = ".mp3"; } else if((hexSub.toLowerCase()).indexOf("fffb9044") > -1) { extension = ".mp3"; } else if((hexSub.toLowerCase()).indexOf("fffb300c") > -1) { extension = ".mp3"; } else if((hexSub.toLowerCase()).indexOf("fff330c0") > -1) { extension = ".mp3"; } else if(hexSub.indexOf("524946") > -1) { extension = ".rmi"; } else if((hexSub.toLowerCase()).indexOf("524d46") > -1) { extension = ".ram"; } else if(hexSub.indexOf("41494646") > -1) { extension = ".aiff"; } else if((hexSub.toLowerCase()).indexOf("4d546864") > -1) { extension = ".mid"; } else if((hexSub.toLowerCase()).indexOf("2e736e64") > -1) { extension = ".au"; } else if((hexSub.toLowerCase()).indexOf("636f6f6c") > -1) { extension = ".stm"; //TE: screamTracker format. } else if((hexSub.toLowerCase()).indexOf("437265617469766520566f6963652046696c65") > -1) { extension = ".voc"; //TE: Creative Voice File. } else if((hexSub.toLowerCase()).indexOf("457874656e646564204d6f64756c65") > -1) { extension = ".xm"; //TE: Extended Module format. } else if((hexSub.toLowerCase()).indexOf("5343524d") > -1) { extension = ".s3m"; } else if((hexSub.toLowerCase()).indexOf("494d50") > -1) { extension = ".it"; } else { extension = ".xxx"; } File file = new File(audioFileDir, currentDN+counter+extension); counter++; file.deleteOnExit(); //TE: deletes the temporary files when JX is shut down. try { FileOutputStream output = new FileOutputStream(file); output.write(bytes); output.close(); } catch (IOException e) { CBUtility.error(CBIntText.get("Problem writing the audio temporary file: ") + file.toString() + "\n" + e); } } /** * This Comparator compares two Files by their lastModified() date. */ public static class FileComparator implements Comparator { /** * This Comparator compares two Files by their lastModified() date. * @param o1 one of the two items to be compared. * @param o2 the other of the items to be compared. * @param the result of the compare (0 if the last Modified date of o1 & o2 are equal, -1 if o1 < o2, 1 if o1 > o2). */ public int compare(Object o1, Object o2) { long a = ((File)o1).lastModified(); long b = ((File)o2).lastModified(); return (a==b)?0:((a<b)?-1:1); } } /** * Sorts an array of files in order of lastModified (oldest to newest). * @param files the array of files to be sorted. * @return the sorted file array. */ public static File[] sortFiles(File[] files) { Arrays.sort(files, new FileComparator()); return files; } /** * Returns the directory that is being used for the caching. * @return the directory. * @author Trudi. */ public static File getCacheDirectory() { return fileDir; } /** * Returns the directory that is being used for the audio caching. * @return the directory. * @author Trudi. */ public static File getAudioCacheDirectory() { return audioFileDir; } /** * Decreases the size of the cache (usually if the cache contains 100 or more temporary files), * by deleting the oldest files until there is only 50 temporary files left in the cache. * It also checks that it deletes all the files pertaining to one entry i.e. if an entry has * four temporary files, this method will try to delete all four even if the minimum level of 50 is * reached (this ensures that the templates refresh correctly). * @author Trudi. */ public static void decreaseCacheSize() { File[] fileArray = fileDir.listFiles(); fileArray = sortFiles(fileArray); //TE: sort the files according to there last modified date (oldest to newest). for (int i=0; i<(fileArray.length - CACHEMIN); i++) //TE: do the following until there is only 50 temp files left. { String fileName = fileArray[i].getName(); for(int x=0; x<allFiles.length; x++) //TE: make sure that when deleting a temp file, that all the temp files pertaining to the same entry are deleted also. { if (allFiles[x].startsWith(fileName.substring(0, ((fileArray[i].toString()).length())-15))) { //TE: if the temp file in the directory at position 'x' starts with the the same name as the temp file listed at // position i of the sorted array (sorted by last modified), minus the last 15 characters (to ensure that the // 'digit+.extension' is cut off the temp name), then delete it!...phew File tempFile = new File(makeDir(), allFiles[x].toString()); tempFile.delete(); } } } decreaseAudioCacheSize(); } /** * Decreases the size of the audio cache (usually if the cache contains 100 or more temporary files), * by deleting the oldest files until there is only 50 temporary files left in the cache. * It also checks that it deletes all the files pertaining to one entry i.e. if an entry has * four temporary files, this method will try to delete all four even if the minimum level of 50 is * reached (this ensures that the templates refresh correctly). * @author Trudi. */ public static void decreaseAudioCacheSize() { File[] fileArray = audioFileDir.listFiles(); fileArray = sortFiles(fileArray); //TE: sort the files according to there last modified date (oldest to newest). for (int i=0; i<(fileArray.length - CACHEMIN); i++) //TE: do the following until there is only 50 temp files left. { String fileName = fileArray[i].getName(); for(int x=0; x<allAudioFiles.length; x++) //TE: make sure that when deleting a temp file, that all the temp files pertaining to the same entry are deleted also. { if (allAudioFiles[x].startsWith(fileName.substring(0, ((fileArray[i].toString()).length())-15))) { //TE: if the temp file in the directory at position 'x' starts with the the same name as the temp file listed at // position i of the sorted array (sorted by last modified), minus the last 15 characters (to ensure that the // 'digit+.extension' is cut off the temp name), then delete it!...phew File tempFile = new File(makeAudioDir(), allAudioFiles[x].toString()); tempFile.delete(); } } } } /** * Creates the temporary directory, calling it 'temp'. * @return the directory. * @author Trudi. */ private static File makeDir() { File dir = new File(dirPath); dir.mkdir(); dir.deleteOnExit(); return dir; } /** * Creates the temporary audio directory, calling it 'temp/audio'. * @return the directory. * @author Trudi. */ private static File makeAudioDir() { File dir = new File(dirPath+File.separator+"audio"); dir.mkdir(); dir.deleteOnExit(); return dir; } /** * Returns the absolute path of the temp directory. * @return the path of the temp directroy. * @author Trudi. */ public static String getDirPath() { return makeDir().getAbsolutePath(); } /** * Sets the path of the temp directory. * @param path the path of where to make the temp directory * @author Trudi. */ public static String setDirPath(String path) { return dirPath = path; } /** * Returns the absolute path of the audio temp directory. * @return the path of the temp directroy. * @author Trudi. */ public static String getAudioDirPath() { return makeAudioDir().getAbsolutePath(); } /** * Gets the byte[] value of a specified attribute value. * @param name the name of the attribute value e.g. 'jpegPhoto'. * @param entry the entry that is to be displayed. * @param entries the number of attributes values (e.g. 'jpegPhoto') pertaining to the entry. * @return the byte array of the attribute value. * @author Trudi. */ private static byte[] getAttByteValue(String name, Attributes entry, int entries) { try { Attribute a = entry.get(name); if (a == null ) { return null; // no pre-existing value, so nothing to do. } if (a.size() == 0 || a.get() == null ) { return null; // no pre-existing value, so nothing to do. } Object o = a.get(entries); //TE: gets the attribute value at a certain position i.e. if 3 attribute values it will get the one at the position stored in entires. if (o instanceof byte[]) { return (byte[]) o; } return null; } catch (NamingException e) { CBUtility.log("Form Value Error getting value for " + name + " value :\n " + e); // not a terminal error. return null; } } /** * Cleans the cache of entries that start with the dn of the entry being modified. * @param currentDN the dn of the entry being modified. * @author Trudi. */ public static void cleanCache(String currentDN) { currentDN = Integer.toString(currentDN.hashCode()); allFiles = makeDir().list(); for(int i=0; i<allFiles.length; i++) { if (allFiles[i].startsWith(currentDN.toString())) //&& allFiles[i].endsWith(extension)) //TE: check that the temp files are of the entry being modified. { File tempFile = new File(fileDir, allFiles[i].toString()); tempFile.delete(); } } allAudioFiles = makeAudioDir().list(); for(int i=0; i<allAudioFiles.length; i++) { if (allAudioFiles[i].startsWith(currentDN.toString())) //&& allFiles[i].endsWith(extension)) //TE: check that the temp files are of the entry being modified. { File tempFile = new File(audioFileDir, allAudioFiles[i].toString()); tempFile.delete(); } } } /** * Cleans the cache of all temporary entries by deleting them. * @author Trudi. */ public static void cleanCache() { allFiles = makeDir().list(); if(allFiles == null) { return; } for(int i=0; i<allFiles.length; i++) { File tempFile = new File(makeDir(), allFiles[i].toString()); tempFile.delete(); } allAudioFiles = makeAudioDir().list(); for(int i=0; i<allAudioFiles.length; i++) { File tempFile = new File(makeAudioDir(), allAudioFiles[i].toString()); tempFile.delete(); } } /** * Sets the maximum cache size @CACHEMAX@ * @param size the maximum cache size. * @author Trudi. */ public static void setMaxCacheSize(int size) { CACHEMAX = size; } /** * Sets the minimum cache size @CACHEMAX@ * @param size the minimum cache size. * @author Trudi. */ public static void setMinCacheSize(int size) { CACHEMIN = size; } }