001    /*
002     * Copyright 2011 The Kuali Foundation.
003     * 
004     * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
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    package org.kuali.kfs.sys.batch;
017    
018    import java.io.File;
019    import java.security.GeneralSecurityException;
020    import java.util.ArrayList;
021    import java.util.HashMap;
022    import java.util.List;
023    
024    import org.apache.commons.lang.StringUtils;
025    import org.kuali.kfs.sys.KFSConstants;
026    import org.kuali.kfs.sys.context.SpringContext;
027    import org.kuali.rice.core.service.EncryptionService;
028    import org.kuali.rice.kns.lookup.keyvalues.KeyValuesFinder;
029    import org.kuali.rice.kns.service.DataDictionaryService;
030    import org.kuali.rice.kns.service.KualiConfigurationService;
031    import org.kuali.rice.core.util.KeyLabelPair;
032    
033    public class BatchFileUtils {
034        public static List<File> retrieveBatchFileLookupRootDirectories() {
035            KualiConfigurationService kualiConfigurationService = SpringContext.getBean(KualiConfigurationService.class);
036            List<File> directories = new ArrayList<File>();
037            String configProperty = kualiConfigurationService.getPropertyString(KFSConstants.BATCH_FILE_LOOKUP_ROOT_DIRECTORIES);
038    
039            String[] directoryNames = StringUtils.split(configProperty, ";");
040            for (String directoryName : directoryNames) {
041                File rootDirectory = new File(directoryName).getAbsoluteFile();
042                directories.add(rootDirectory);
043            }
044    
045            // sanity check: make sure directories are set up so that they will not present problems for pathRelativeToRootDirectory and
046            // resolvePathToAbsolutePath methods
047            for (int i = 0; i < directories.size(); i++) {
048                for (int j = i + 1; j < directories.size(); j++) {
049                    File directoryI = directories.get(i);
050                    File directoryJ = directories.get(j);
051    
052                    if (isPrefixOfAnother(directoryI.getAbsolutePath(), directoryJ.getAbsolutePath())) {
053                        throw new RuntimeException("Cannot have any two directories in config property batch.file.lookup.root.directories that have absolute paths that are prefix of another");
054                    }
055                    if (isPrefixOfAnother(directoryI.getName(), directoryJ.getName())) {
056                        throw new RuntimeException("Cannot have any two directories in config property batch.file.lookup.root.directories that have names that are prefix of another");
057                    }
058                }
059            }
060            return directories;
061        }
062    
063        private static boolean isPrefixOfAnother(String str1, String str2) {
064            return str1.startsWith(str2) || str2.startsWith(str1);
065        }
066    
067        /**
068         * returns a path relative to the appropriate lookup root directory, while including the name of the root directory for example,
069         * if the parameter is "c:\opt\staging\gl\somefile.txt" and the roots are "c:\opt\reports;c:\opt\staging", it will return
070         * "staging\gl\somefile.txt" (the system-specific path separator will be used). If there are multiple matching roots, then the
071         * first one to be matched will take precedence
072         * 
073         * @param absolutePath an absolute path for a file/directory
074         */
075        public static String pathRelativeToRootDirectory(String absolutePath) {
076            for (File rootDirectory : retrieveBatchFileLookupRootDirectories()) {
077                if (absolutePath.startsWith(rootDirectory.getAbsolutePath())) {
078                    return StringUtils.replaceOnce(absolutePath, rootDirectory.getAbsolutePath(), rootDirectory.getName());
079                }
080            }
081            throw new RuntimeException("Unable to find appropriate root directory)");
082        }
083    
084    
085        /**
086         * @param path a path string that was generated by {@link #pathRelativeToRootDirectory(String)}
087         * @return an absolute path, including the root directory
088         */
089        public static String resolvePathToAbsolutePath(String path) {
090            for (File rootDirectory : retrieveBatchFileLookupRootDirectories()) {
091                if (path.startsWith(rootDirectory.getName())) {
092                    return new File(StringUtils.replaceOnce(path, rootDirectory.getName(), rootDirectory.getAbsolutePath())).getAbsolutePath();
093                }
094            }
095            throw new RuntimeException("Cannot resolve to absolute path");
096        }
097    
098        public static boolean isDirectoryAccessible(String directory) {
099            List<String> pathNames = null;
100    
101            Class<? extends KeyValuesFinder> keyValuesFinderClass = SpringContext.getBean(DataDictionaryService.class).getAttributeValuesFinderClass(BatchFile.class, "path");
102            try {
103                if (keyValuesFinderClass != null) {
104                    KeyValuesFinder valuesGenerator = keyValuesFinderClass.newInstance();
105                    pathNames = new ArrayList<String>();
106    
107                    List<KeyLabelPair> keyValues = valuesGenerator.getKeyValues();
108                    for (KeyLabelPair keyValue : keyValues) {
109                        pathNames.add(new File(resolvePathToAbsolutePath((String) keyValue.getKey())).getAbsolutePath());
110                    }
111                }
112            }
113            catch (IllegalAccessException e) {
114                throw new RuntimeException("can't instiantiate class " + keyValuesFinderClass, e);
115            }
116            catch (InstantiationException e) {
117                throw new RuntimeException("can't instiantiate class " + keyValuesFinderClass, e);
118            }
119    
120            File directoryAbsolute = new File(directory).getAbsoluteFile();
121            String directoryAbsolutePath = directoryAbsolute.getAbsolutePath();
122            if (pathNames != null) {
123                if (!pathNames.contains(directoryAbsolutePath)) {
124                    return false;
125                }
126            }
127    
128            List<File> rootDirectories = retrieveBatchFileLookupRootDirectories();
129            for (File rootDirectory : rootDirectories) {
130                if (isSuperDirectoryOf(rootDirectory, directoryAbsolute)) {
131                    return true;
132                }
133            }
134            return false;
135        }
136    
137        public static boolean isSuperDirectoryOf(File superDirectory, File directory) {
138            superDirectory = superDirectory.getAbsoluteFile();
139    
140            while (directory != null) {
141                directory = directory.getAbsoluteFile();
142                if (directory.equals(superDirectory)) {
143                    return true;
144                }
145                directory = directory.getParentFile();
146            }
147            return false;
148        }
149    }