001    /*
002     * $Revision: 13744 $ $Date: 2008-07-14 15:05:49 +0200 (Mo, 14 Jul 2008) $ This file is part of M y C o R e See http://www.mycore.de/ for details. This program
003     * is free software; you can use it, redistribute it and / or modify it under the terms of the GNU General Public License (GPL) as published by the Free
004     * Software Foundation; either version 2 of the License or (at your option) any later version. This program is distributed in the hope that it will be useful,
005     * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
006     * more details. You should have received a copy of the GNU General Public License along with this program, in a file called gpl.txt or license.txt. If not,
007     * write to the Free Software Foundation Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA
008     */
009    
010    package org.mycore.datamodel.ifs;
011    
012    import java.io.File;
013    import java.io.FileInputStream;
014    import java.io.FileNotFoundException;
015    
016    import org.mycore.common.MCRArgumentChecker;
017    import org.mycore.common.MCRException;
018    import org.mycore.common.MCRUtils;
019    
020    /**
021     * Imports or exports complete directory trees with all contained files and subdirectories between the local host's filesystem and the internal MCRDirectory
022     * structures.
023     * 
024     * @author Frank Lützenkirchen
025     * @version $Revision: 13744 $ $Date: 2008-07-14 15:05:49 +0200 (Mo, 14 Jul 2008) $
026     */
027    public class MCRFileImportExport {
028        /**
029         * Imports the contents of a local file or directory into a newly created MCRDirectory that is owned by the given owner ID. The new MCRDirectory will have
030         * the same name as the owner ID. If the local object is a file, a MCRFile with the same name will be created or updated in that MCRDirectory. If the local
031         * object is a directory, all contained subdirectories and files will be imported into the newly created MCRDirectory. That means that after finishing this
032         * method, the complete directory structure will have been imported and mapped from the local filesystem's structure. The method checks the contents of each
033         * local file to be imported. If the file's content has not changed for existing files, the internal MCRFile will not be updated. If there is any exception
034         * while importing the local contents, the system will try to undo this operation by completely deleting all content that was imported so far.
035         * 
036         * @param local
037         *            the local file or directory to be imported
038         * @param ownerID
039         *            the ID of the logical owner of the content that will be stored
040         * @return a new MCRDirectory that will contain all imported files and directories as instances of MCRFilesystemNode children.
041         */
042        public static MCRDirectory importFiles(File local, String ownerID) {
043            MCRArgumentChecker.ensureNotEmpty(ownerID, "owner ID");
044    
045            // Create new parent directory
046            MCRDirectory dir = new MCRDirectory(ownerID, ownerID);
047    
048            try // Try to import local content into this new directory
049            {
050                importFiles(local, dir);
051            } catch (MCRException mex) // If anything goes wrong
052            {
053                try {
054                    dir.delete();
055                } // Try to delete all content stored so far
056                catch (Exception ignored) {
057                }
058    
059                throw mex;
060            }
061    
062            return dir;
063        }
064    
065        /**
066         * Imports the contents of a local file or directory into an existing MCRDirectory that is owned by the given owner ID. The new MCRDirectory will have the
067         * same name as the owner ID. If the local object is a file, a MCRFile with the same name will be created or updated in that MCRDirectory. If the local
068         * object is a directory, all contained subdirectories and files will be imported into the newly created MCRDirectory. That means that after finishing this
069         * method, the complete directory structure will have been imported and mapped from the local filesystem's structure. The method checks the contents of each
070         * local file to be imported. If the file's content has not changed for existing files, the internal MCRFile will not be updated. If there is any exception
071         * while importing the local contents, the system will stop with the last state and break the work.
072         * 
073         * @param local
074         *            the local file or directory to be imported
075         * @param ownerID
076         *            the ID of the logical owner of the content that will be stored
077         * @return a new MCRDirectory that will contain all imported files and directories as instances of MCRFilesystemNode children.
078         */
079        public static MCRDirectory addFiles(File local, String ownerID) {
080            MCRArgumentChecker.ensureNotEmpty(ownerID, "owner ID");
081    
082            // Get the existing parent directory
083            MCRDirectory dir = MCRDirectory.getRootDirectory(ownerID);
084    
085            try // Try to import local content into this new directory
086            {
087                importFiles(local, dir);
088            } catch (MCRException mex) // If anything goes wrong
089            {
090                throw mex;
091            }
092    
093            return dir;
094        }
095    
096        /**
097         * Imports the contents of a local file or directory into the MyCoRe Internal Filesystem. If the local object is a file, a MCRFile with the same name will
098         * be created or updated in the given MCRDirectory. If the local object is a directory, all contained subdirectories and files will be imported into the
099         * given MCRDirectory. That means that after finishing this method, the complete directory structure will have been imported and mapped from the local
100         * filesystem's structure. The method checks the contents of each local file to be imported. If the file's content has not changed for existing files, the
101         * internal MCRFile will not be updated. If an internal directory is updated from a local directory, new files will be added, existing files will be updated
102         * if necessary, but files that already exist in the given MCRDirectory but not in the local filesystem will be kept and will not be deleted.
103         * 
104         * @param local
105         *            the local file or directory
106         * @param dir
107         *            an existing MCRDirectory where to store the imported contents of the local filesystem.
108         */
109        public static void importFiles(File local, MCRDirectory dir) {
110            MCRArgumentChecker.ensureNotNull(local, "local file");
111    
112            String path = local.getPath();
113            String name = local.getName();
114    
115            MCRArgumentChecker.ensureIsTrue(local.exists(), "Not found: " + path);
116            MCRArgumentChecker.ensureIsTrue(local.canRead(), "Not readable: " + path);
117    
118            if (local.isFile()) // Import a local file
119            {
120                MCRFilesystemNode existing = dir.getChild(name);
121                MCRFile file = null;
122    
123                // If internal directory with same name exists
124                if (existing instanceof MCRDirectory) {
125                    existing.delete(); // delete it
126                    existing = null;
127                }
128    
129                if (existing == null) { // Create new, empty MCRFile
130                    file = new MCRFile(name, dir, false);
131                } else {
132                    file = (MCRFile) existing; // Update existing MCRFile
133    
134                    // Determine MD5 checksum of local file
135                    FileInputStream fin = null;
136    
137                    try {
138                        fin = new FileInputStream(local);
139                    } catch (FileNotFoundException willNotBeThrown) {
140                    }
141    
142                    MCRContentInputStream cis = new MCRContentInputStream(fin);
143    
144                    if (!MCRUtils.copyStream(cis, null)) {
145                        String msg = "Error while reading local file " + local.getPath();
146                        throw new MCRException(msg);
147                    }
148    
149                    String local_md5 = cis.getMD5String();
150    
151                    // If file content of local file has not changed, do not load it
152                    // again
153                    if (file.getMD5().equals(local_md5)) {
154                        return;
155                    }
156                }
157    
158                // Store file content
159                file.setContentFrom(local);
160            } else {
161                File[] files = local.listFiles();
162    
163                // For each local child node
164                for (int i = 0; i < files.length; i++) {
165                    local = files[i];
166                    name = local.getName();
167    
168                    MCRDirectory internalDir = dir;
169    
170                    if (local.isDirectory()) {
171                        MCRFilesystemNode existing = dir.getChild(name);
172    
173                        if (existing instanceof MCRFile) { // If there is an
174    
175                            // existing MCRFile with
176                            // same name
177                            existing.delete(); // delete that existing MCRFile
178                            existing = null;
179                        }
180    
181                        if (existing == null) { // Create new directory
182                            internalDir = new MCRDirectory(name, dir, false);
183                        } else {
184                            internalDir = (MCRDirectory) existing;
185                        }
186                    }
187    
188                    importFiles(local, internalDir); // Recursively import
189                }
190            }
191        }
192    
193        /**
194         * Exports all contents of the given MCRDirectory to the local filesystem, including all subdirectories and stored files. If the local object is a file, the
195         * parent directory of that file will be used for exporting.
196         * 
197         * @param local
198         *            the local directory where to export the contents to
199         * @param dir
200         *            the directory thats contents should be exported
201         */
202        public static void exportFiles(MCRDirectory dir, File local) throws MCRException {
203            MCRArgumentChecker.ensureNotNull(dir, "internal directory");
204            MCRArgumentChecker.ensureNotNull(local, "local file");
205    
206            String path = local.getPath();
207            MCRArgumentChecker.ensureIsTrue(local.canWrite(), "Not writeable: " + path);
208    
209            // If local is file, use its parent instead
210            if (local.isFile()) {
211                local = local.getParentFile();
212            }
213    
214            MCRFilesystemNode[] children = dir.getChildren();
215    
216            for (int i = 0; i < children.length; i++) {
217                if (children[i] instanceof MCRFile) {
218                    MCRFile internalFile = (MCRFile) (children[i]);
219                    String name = internalFile.getName();
220    
221                    File localFile = new File(local, name);
222    
223                    try {
224                        internalFile.getContentTo(localFile);
225                    } catch (Exception ex) {
226                        throw new MCRException("Can't get file content.", ex);
227                    }
228                } else {
229                    MCRDirectory internalDir = (MCRDirectory) (children[i]);
230                    String name = internalDir.getName();
231    
232                    File localDir = new File(local, name);
233    
234                    if (!localDir.exists()) {
235                        localDir.mkdir();
236                    }
237    
238                    exportFiles(internalDir, localDir);
239                }
240            }
241        }
242    }