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