001/*
002 * (C) Copyright 2006-2009 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Nuxeo
016 */
017
018package org.nuxeo.ecm.platform.publisher.impl.localfs;
019
020import java.io.BufferedReader;
021import java.io.BufferedWriter;
022import java.io.File;
023import java.io.FileReader;
024import java.io.FileWriter;
025import java.io.IOException;
026import java.util.ArrayList;
027import java.util.List;
028import java.util.Map;
029
030import org.apache.commons.io.FileUtils;
031import org.nuxeo.ecm.core.api.CoreSession;
032import org.nuxeo.ecm.core.api.DocumentLocation;
033import org.nuxeo.ecm.core.api.DocumentModel;
034import org.nuxeo.ecm.core.api.NuxeoException;
035import org.nuxeo.ecm.platform.publisher.api.AbstractBasePublicationTree;
036import org.nuxeo.ecm.platform.publisher.api.PublicationNode;
037import org.nuxeo.ecm.platform.publisher.api.PublicationTree;
038import org.nuxeo.ecm.platform.publisher.api.PublishedDocument;
039import org.nuxeo.ecm.platform.publisher.api.PublishedDocumentFactory;
040
041public class LocalFSPublicationTree extends AbstractBasePublicationTree implements PublicationTree {
042    private static final long serialVersionUID = 1L;
043
044    public static final String INDEX_FILENAME = "fspublication.index";
045
046    public static final String INDEX_FILENAME_TMP = INDEX_FILENAME + ".tmp";
047
048    @Override
049    public void initTree(String sid, CoreSession coreSession, Map<String, String> parameters,
050            PublishedDocumentFactory factory, String configName, String title) {
051        super.initTree(sid, coreSession, parameters, factory, configName, title);
052        try {
053            rootNode = new FSPublicationNode(rootPath, getTreeConfigName(), sid);
054        } catch (IllegalArgumentException e) {
055            throw new NuxeoException(e);
056        }
057    }
058
059    @Override
060    protected PublishedDocumentFactory getDefaultFactory() {
061        return new FSPublishedDocumentFactory();
062    }
063
064    @Override
065    protected String getDefaultRootPath() {
066        return "/";
067    }
068
069    protected void findDocs(List<PublishedDocument> pubDocs, File container, DocumentLocation docLoc) {
070        for (File child : container.listFiles()) {
071            if (child.isDirectory()) {
072                findDocs(pubDocs, child, docLoc);
073            } else {
074                try {
075                    PublishedDocument pubDoc = new FSPublishedDocument(child);
076                    if (pubDoc.getSourceRepositoryName().equals(docLoc.getServerName())
077                            && pubDoc.getSourceDocumentRef().equals(docLoc.getDocRef())) {
078                        pubDocs.add(pubDoc);
079                    }
080                } catch (NotFSPublishedDocumentException e) {
081                    // NOP
082                }
083            }
084        }
085    }
086
087    public List<PublishedDocument> getExistingPublishedDocument(DocumentLocation docLoc) {
088        List<PublishedDocument> pubDocs = null;
089        pubDocs = loadExistingPublishedDocumentFromIndex(docLoc);
090        if (pubDocs == null) {
091            pubDocs = new ArrayList<PublishedDocument>();
092            File root = new File(getPath());
093            findDocs(pubDocs, root, docLoc);
094
095            // create the index
096            createIndex(pubDocs);
097        }
098        return pubDocs;
099    }
100
101    private List<PublishedDocument> loadExistingPublishedDocumentFromIndex(DocumentLocation docLoc)
102            {
103        File indexFile = new File(rootPath, INDEX_FILENAME);
104        if (!indexFile.exists() || !indexFile.isFile()) {
105            return null;
106        }
107
108        List<PublishedDocument> pubDocs = new ArrayList<PublishedDocument>();
109        BufferedReader reader = null;
110        try {
111            reader = new BufferedReader(new FileReader(indexFile));
112            String filePath;
113            while ((filePath = reader.readLine()) != null) {
114                File file = new File(filePath);
115                if (file.exists()) {
116                    PublishedDocument pubDoc = new FSPublishedDocument(file);
117                    if (pubDoc.getSourceRepositoryName().equals(docLoc.getServerName())
118                            && pubDoc.getSourceDocumentRef().equals(docLoc.getDocRef())) {
119                        pubDocs.add(pubDoc);
120                    }
121                }
122            }
123        } catch (IOException e) {
124            throw new NuxeoException(e);
125        } finally {
126            if (reader != null) {
127                try {
128                    reader.close();
129                } catch (IOException e) {
130                }
131            }
132        }
133
134        return pubDocs;
135    }
136
137    private void createIndex(List<PublishedDocument> pubDocs) {
138        File indexFile = new File(rootPath, INDEX_FILENAME);
139        BufferedWriter writer = null;
140        try {
141            writer = new BufferedWriter(new FileWriter(indexFile));
142            for (PublishedDocument pubDoc : pubDocs) {
143                writer.write(pubDoc.getPath());
144                writer.newLine();
145            }
146        } catch (IOException e) {
147            throw new NuxeoException(e);
148        } finally {
149            if (writer != null) {
150                try {
151                    writer.flush();
152                    writer.close();
153                } catch (IOException e) {
154                }
155            }
156        }
157    }
158
159    private void addToIndex(PublishedDocument pubDoc) {
160        File fileIndex = new File(rootPath, INDEX_FILENAME);
161        File fileIndexTmp = new File(rootPath, INDEX_FILENAME_TMP);
162
163        BufferedReader reader = null;
164        BufferedWriter writer = null;
165        try {
166            if (!fileIndex.exists()) {
167                fileIndex.createNewFile();
168            }
169
170            reader = new BufferedReader(new FileReader(fileIndex));
171            writer = new BufferedWriter(new FileWriter(fileIndexTmp));
172            String pathToAdd = pubDoc.getPath();
173            String line;
174            boolean pathAlreadyFound = false;
175            while ((line = reader.readLine()) != null) {
176                if (line.equals(pathToAdd)) {
177                    pathAlreadyFound = true;
178                }
179                writer.write(line);
180                writer.newLine();
181            }
182            if (!pathAlreadyFound) {
183                writer.write(pathToAdd);
184                writer.newLine();
185            }
186        } catch (IOException e) {
187            throw new NuxeoException(e);
188        } finally {
189            if (reader != null) {
190                try {
191                    reader.close();
192                } catch (IOException e) {
193                }
194            }
195
196            if (writer != null) {
197                try {
198                    writer.flush();
199                    writer.close();
200                } catch (IOException e) {
201                }
202            }
203        }
204
205        // move the tmp index file to the index file after closing both stream
206        if (fileIndex.delete()) {
207            moveFile(fileIndexTmp, fileIndex);
208        }
209    }
210
211    private void moveFile(File srcFile, File destFile) {
212        try {
213            FileUtils.moveFile(srcFile, destFile);
214        } catch (IOException e) {
215            throw new NuxeoException(e);
216        }
217    }
218
219    private void removeFromIndex(PublishedDocument pubDoc) {
220        File fileIndex = new File(rootPath, INDEX_FILENAME);
221        File fileIndexTmp = new File(rootPath, INDEX_FILENAME_TMP);
222
223        BufferedReader reader = null;
224        BufferedWriter writer = null;
225        try {
226            reader = new BufferedReader(new FileReader(fileIndex));
227            writer = new BufferedWriter(new FileWriter(fileIndexTmp));
228            String pathToRemove = pubDoc.getPath();
229            String line;
230            while ((line = reader.readLine()) != null) {
231                if (!pathToRemove.equals(line)) {
232                    writer.write(line);
233                    writer.newLine();
234                }
235            }
236        } catch (IOException e) {
237            throw new NuxeoException(e);
238        } finally {
239            if (reader != null) {
240                try {
241                    reader.close();
242                } catch (IOException e) {
243                }
244            }
245
246            if (writer != null) {
247                try {
248                    writer.flush();
249                    writer.close();
250                } catch (IOException e) {
251                }
252            }
253        }
254
255        // move the tmp index file to the index file after closing both stream
256        if (fileIndex.delete()) {
257            moveFile(fileIndexTmp, fileIndex);
258        }
259    }
260
261    public PublicationNode getNodeByPath(String path) {
262        return new FSPublicationNode(path, getTreeConfigName(), getSessionId());
263    }
264
265    @Override
266    public PublishedDocument publish(DocumentModel doc, PublicationNode targetNode) {
267        PublishedDocument pubDoc = super.publish(doc, targetNode);
268        addToIndex(pubDoc);
269        return pubDoc;
270    }
271
272    @Override
273    public PublishedDocument publish(DocumentModel doc, PublicationNode targetNode, Map<String, String> params)
274            {
275        PublishedDocument pubDoc = super.publish(doc, targetNode, params);
276        addToIndex(pubDoc);
277        return pubDoc;
278    }
279
280    public void unpublish(DocumentModel doc, PublicationNode targetNode) {
281        File container = new File(targetNode.getPath());
282        for (File child : container.listFiles()) {
283            try {
284                unpublish(doc, child);
285            } catch (NotFSPublishedDocumentException e) {
286                // NOP
287            }
288        }
289    }
290
291    private void unpublish(DocumentModel doc, File file) throws NotFSPublishedDocumentException {
292        FSPublishedDocument pubDoc = new FSPublishedDocument(file);
293        if (pubDoc.getSourceRepositoryName().equals(doc.getRepositoryName())
294                && pubDoc.getSourceDocumentRef().equals(doc.getRef())) {
295            new File(pubDoc.getPersistPath()).delete();
296            removeFromIndex(pubDoc);
297        }
298    }
299
300    public void unpublish(PublishedDocument pubDoc) {
301        if (!accept(pubDoc)) {
302            return;
303        }
304        FSPublishedDocument fsPublishedDocument = (FSPublishedDocument) pubDoc;
305        new File(fsPublishedDocument.getPersistPath()).delete();
306        removeFromIndex(pubDoc);
307    }
308
309    public void release() {
310    }
311
312    protected boolean accept(PublishedDocument publishedDocument) {
313        return publishedDocument instanceof FSPublishedDocument;
314    }
315
316}