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 }