001    /*
002     * 
003     * $Revision: 14986 $ $Date: 2009-03-20 21:41:45 +0100 (Fri, 20 Mar 2009) $
004     *
005     * This file is part of ***  M y C o R e  ***
006     * See http://www.mycore.de/ for details.
007     *
008     * This program is free software; you can use it, redistribute it
009     * and / or modify it under the terms of the GNU General Public License
010     * (GPL) as published by the Free Software Foundation; either version 2
011     * of the License or (at your option) any later version.
012     *
013     * This program is distributed in the hope that it will be useful, but
014     * WITHOUT ANY WARRANTY; without even the implied warranty of
015     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016     * GNU General Public License for more details.
017     *
018     * You should have received a copy of the GNU General Public License
019     * along with this program, in a file called gpl.txt or license.txt.
020     * If not, write to the Free Software Foundation Inc.,
021     * 59 Temple Place - Suite 330, Boston, MA  02111-1307 USA
022     */
023    
024    package org.mycore.frontend.fileupload;
025    
026    import java.io.InputStream;
027    import java.util.Collection;
028    import java.util.StringTokenizer;
029    
030    import org.apache.log4j.Logger;
031    import org.hibernate.Transaction;
032    import org.mycore.access.MCRAccessInterface;
033    import org.mycore.access.MCRAccessManager;
034    import org.mycore.backend.hibernate.MCRHIBConnection;
035    import org.mycore.common.MCRConfiguration;
036    import org.mycore.common.MCRException;
037    import org.mycore.datamodel.ifs.MCRDirectory;
038    import org.mycore.datamodel.ifs.MCRFile;
039    import org.mycore.datamodel.ifs.MCRFilesystemNode;
040    import org.mycore.datamodel.metadata.MCRDerivate;
041    import org.mycore.datamodel.metadata.MCRMetaIFS;
042    import org.mycore.datamodel.metadata.MCRMetaLinkID;
043    import org.mycore.datamodel.metadata.MCRObjectID;
044    
045    /**
046     * handles uploads via the UploadApplet and store files directly into the IFS.
047     * 
048     * @author Thomas Scheffler (yagee)
049     * 
050     * @version $Revision: 14986 $ $Date: 2009-03-20 21:41:45 +0100 (Fri, 20 Mar 2009) $
051     * 
052     * @see MCRUploadHandler
053     */
054    public class MCRUploadHandlerIFS extends MCRUploadHandler {
055    
056        protected MCRDerivate derivate;
057    
058        protected MCRDirectory rootDir;
059    
060        protected boolean newDerivate = true;
061    
062        private Transaction tx;
063    
064        private static final String ID_TYPE = "derivate";
065    
066        private static final String PROJECT = MCRConfiguration.instance().getString("MCR.SWF.Project.ID", "MCR");
067    
068        private static final Logger LOGGER = Logger.getLogger(MCRUploadHandlerIFS.class);
069    
070        private static final MCRConfiguration CONFIG = MCRConfiguration.instance();
071    
072        public MCRUploadHandlerIFS(String docId, String derId, String url) {
073            super();
074            this.url = url;
075            init(docId, derId);
076        }
077    
078        protected void init(String docId, String derId) {
079            if (derId == null) {
080                // create new derivate
081                LOGGER.debug("derId=null create derivate with next free ID");
082                createNewDerivate(docId, getFreeDerivateID());
083            } else {
084                if (MCRDerivate.existInDatastore(derId)) {
085                    LOGGER.debug("Derivate allready exists: " + derId);
086                    newDerivate = false;
087                    derivate = new MCRDerivate();
088                    derivate.receiveFromDatastore(derId);
089                } else {
090                    // create new derivate with given ID
091                    LOGGER.debug("derId='" + derId + "' create derivate with that ID");
092                    createNewDerivate(docId, new MCRObjectID(derId));
093                }
094            }
095        }
096    
097        /**
098         * Start Upload for MyCoRe
099         */
100        public void startUpload(int numFiles) throws Exception {
101            if (newDerivate) {
102                LOGGER.debug("Create new derivate with id: " + derivate.getId());
103                derivate.createInDatastore();
104            }
105            rootDir = getRootDir(derivate.getId().toString());
106        }
107    
108        /**
109         * Message from UploadApplet If you want all files transfered omit this
110         * method
111         * 
112         * @param path
113         *            file name
114         * @param checksum
115         *            md5 checksum of of file
116         * @param length
117         *            the length of the file in bytes (file size)
118         * 
119         * @return true transfer file false don't send file
120         * 
121         */
122        public boolean acceptFile(String path, String checksum, long length) throws Exception {
123            MCRFilesystemNode child = rootDir.getChildByPath(path);
124            if (!(child instanceof MCRFile)) {
125                return true;
126            }
127            MCRFile file = (MCRFile) child;
128            return !checksum.equals(file.getMD5());
129        }
130    
131        public synchronized long receiveFile(String path, InputStream in, long length, String md5) throws Exception {
132            try {
133                LOGGER.debug("adding file: " + path);
134                startTransaction();
135                MCRFile file = getNewFile(path);
136                commitTransaction();
137                long sizeDiff = file.setContentFrom(in, false);
138                startTransaction();
139                file.storeContentChange(sizeDiff);
140                commitTransaction();
141    
142                long myLength = file.getSize();
143                if (myLength >= length)
144                    return myLength;
145                else {
146                    file.delete(); // Incomplete file transfer, user canceled upload
147                    return 0;
148                }
149            } catch (Exception e) {
150                LOGGER.error("Error while uploading file: "+path,e);
151                try {
152                    rollbackTransaction();
153                } catch (Exception e2) {
154                    LOGGER.debug("Error while rolling back transaction",e);
155                }
156                return 0;
157            }
158        }
159    
160        /**
161         * Finish upload, store derivate
162         * 
163         */
164        public void finishUpload() throws Exception {
165            String mainfile = getMainFilePath(rootDir);
166            if (newDerivate) {
167                derivate.getDerivate().getInternals().setMainDoc(mainfile);
168                derivate.updateXMLInDatastore();
169                setDefaultPermissions(derivate.getId());
170            } else {
171                String mf = derivate.getDerivate().getInternals().getMainDoc();
172                if (mf.trim().length() == 0) {
173                    derivate.getDerivate().getInternals().setMainDoc(mainfile);
174                    derivate.updateXMLInDatastore();
175                }
176            }
177        }
178    
179        private static MCRObjectID getFreeDerivateID() {
180            MCRObjectID derivateID = new MCRObjectID();
181            derivateID.setNextFreeId(PROJECT + '_' + ID_TYPE);
182            return derivateID;
183        }
184    
185        protected void createNewDerivate(String docId, MCRObjectID newDerID) {
186            newDerivate = true;
187            derivate = new MCRDerivate();
188            derivate.setId(newDerID);
189            String schema = CONFIG.getString("MCR.Metadata.Config.derivate", "datamodel-derivate.xml").replaceAll(".xml", ".xsd");
190            derivate.setSchema(schema);
191            derivate.setLabel("data object from " + docId);
192            // set link to Object
193            MCRMetaLinkID linkId = new MCRMetaLinkID();
194            linkId.setSubTag("linkmeta");
195            linkId.setReference(docId, null, null);
196            MCRMetaIFS ifs = new MCRMetaIFS();
197            ifs.setSubTag("internal");
198            ifs.setSourcePath(null);
199            derivate.getDerivate().setInternals(ifs);
200            derivate.getDerivate().setLinkMeta(linkId);
201        }
202    
203        private MCRFile getNewFile(String path) {
204            if (path.indexOf("/") == -1) {
205                return new MCRFile(path, rootDir);
206            }
207            StringTokenizer tok = new StringTokenizer(path, "/");
208            MCRDirectory parent = rootDir;
209            while (tok.hasMoreTokens()) {
210                String child = tok.nextToken();
211                if (parent.hasChild(child)) {
212                    MCRFilesystemNode childNode = parent.getChild(child);
213                    if ((childNode instanceof MCRFile) && !tok.hasMoreTokens()) {
214                        return (MCRFile) childNode;
215                    } else if (childNode instanceof MCRDirectory) {
216                        parent = (MCRDirectory) childNode;
217                    } else {
218                        // obviously a file should not contain any other files
219                        return null;
220                    }
221                } else {
222                    if (tok.hasMoreTokens()) {
223                        parent = new MCRDirectory(child, parent);
224                    } else {
225                        // NOTE: How should we handle empty directories?
226                        return new MCRFile(child, parent);
227                    }
228                }
229            }
230            LOGGER.error("Please investigate getNewFile() method in IFS upload handler. Server shouldn't get to this point!");
231            return null;
232        }
233    
234        private static MCRDirectory getRootDir(String derID) {
235            MCRFilesystemNode root = MCRFilesystemNode.getRootNode(derID);
236            if (!(root instanceof MCRDirectory)) {
237                root = new MCRDirectory(derID, derID);
238            }
239            MCRDirectory rootDir = (MCRDirectory) root;
240            return rootDir;
241        }
242    
243        protected static String getMainFilePath(MCRDirectory root) {
244            MCRDirectory parent = root;
245            while (parent.hasChildren()) {
246                MCRFilesystemNode[] children = parent.getChildren(MCRDirectory.SORT_BY_NAME);
247                if (children[0] instanceof MCRDirectory) {
248                    parent = (MCRDirectory) children[0];
249                }
250                for (int i = 0; i < children.length; i++) {
251                    if (children[i] instanceof MCRFile)
252                        return children[i].getAbsolutePath().substring(1);
253                }
254            }
255            return "";
256        }
257    
258        protected static void setDefaultPermissions(MCRObjectID derID) {
259            if (CONFIG.getBoolean("MCR.Access.AddDerivateDefaultRule", true)) {
260                MCRAccessInterface AI = MCRAccessManager.getAccessImpl();
261                Collection<String> configuredPermissions = AI.getAccessPermissionsFromConfiguration();
262                for (String permission : configuredPermissions) {
263                    MCRAccessManager.addRule(derID, permission, MCRAccessManager.getTrueRule(), "default derivate rule");
264                }
265            }
266        }
267    
268        protected void startTransaction() {
269            LOGGER.debug("Starting transaction");
270            if (tx == null || !tx.isActive())
271                tx = MCRHIBConnection.instance().getSession().beginTransaction();
272            else
273                throw new MCRException("Transaction already started");
274        }
275    
276        protected void commitTransaction() {
277            LOGGER.debug("Committing transaction");
278            if (tx != null) {
279                tx.commit();
280                tx = null;
281            } else
282                throw new NullPointerException("Cannot commit transaction");
283        }
284    
285        protected void rollbackTransaction() {
286            LOGGER.debug("Rolling back transaction");
287            if (tx != null) {
288                tx.rollback();
289                tx = null;
290            } else
291                throw new NullPointerException("Cannot rollback transaction");
292        }
293    
294    }