001/*
002 * (C) Copyright 2006-2010 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 *     Thierry Delprat
018 */
019package org.nuxeo.apidoc.snapshot;
020
021import java.io.IOException;
022import java.io.InputStream;
023import java.io.OutputStream;
024import java.io.Serializable;
025import java.util.ArrayList;
026import java.util.Collections;
027import java.util.HashMap;
028import java.util.List;
029import java.util.Map;
030
031import javax.servlet.http.HttpServletRequest;
032
033import org.apache.commons.logging.Log;
034import org.apache.commons.logging.LogFactory;
035import org.nuxeo.apidoc.api.BundleGroup;
036import org.nuxeo.apidoc.api.BundleInfo;
037import org.nuxeo.apidoc.api.ComponentInfo;
038import org.nuxeo.apidoc.api.ExtensionInfo;
039import org.nuxeo.apidoc.api.ExtensionPointInfo;
040import org.nuxeo.apidoc.api.NuxeoArtifact;
041import org.nuxeo.apidoc.api.OperationInfo;
042import org.nuxeo.apidoc.api.ServiceInfo;
043import org.nuxeo.apidoc.introspection.RuntimeSnapshot;
044import org.nuxeo.apidoc.repository.RepositoryDistributionSnapshot;
045import org.nuxeo.apidoc.repository.SnapshotPersister;
046import org.nuxeo.ecm.core.api.CoreSession;
047import org.nuxeo.ecm.core.api.DocumentModel;
048import org.nuxeo.ecm.core.api.DocumentRef;
049import org.nuxeo.ecm.core.api.NuxeoException;
050import org.nuxeo.ecm.core.api.PathRef;
051import org.nuxeo.ecm.core.io.DocumentPipe;
052import org.nuxeo.ecm.core.io.DocumentReader;
053import org.nuxeo.ecm.core.io.DocumentWriter;
054import org.nuxeo.ecm.core.io.impl.DocumentPipeImpl;
055import org.nuxeo.ecm.core.io.impl.plugins.DocumentModelWriter;
056import org.nuxeo.ecm.core.io.impl.plugins.DocumentTreeReader;
057import org.nuxeo.ecm.core.io.impl.plugins.NuxeoArchiveReader;
058import org.nuxeo.ecm.core.io.impl.plugins.NuxeoArchiveWriter;
059import org.nuxeo.runtime.model.DefaultComponent;
060
061import static java.util.Collections.reverseOrder;
062import static java.util.Comparator.comparing;
063import static org.nuxeo.ecm.core.api.validation.DocumentValidationService.CTX_MAP_KEY;
064import static org.nuxeo.ecm.core.api.validation.DocumentValidationService.Forcing.TURN_OFF;
065
066public class SnapshotManagerComponent extends DefaultComponent implements SnapshotManager {
067
068    protected volatile DistributionSnapshot runtimeSnapshot;
069
070    public static final String RUNTIME = "current";
071
072    public static final String RUNTIME_ADM = "adm";
073
074    protected static final String IMPORT_TMP = "tmpImport";
075
076    protected static final Log log = LogFactory.getLog(SnapshotManagerComponent.class);
077
078    protected final SnapshotPersister persister = new SnapshotPersister();
079
080    @Override
081    public DistributionSnapshot getRuntimeSnapshot() {
082        if (runtimeSnapshot == null) {
083            synchronized (this) {
084                if (runtimeSnapshot == null) {
085                    runtimeSnapshot = RuntimeSnapshot.build();
086                }
087            }
088        }
089        return runtimeSnapshot;
090    }
091
092    @Override
093    public DistributionSnapshot getSnapshot(String key, CoreSession session) {
094        if (key == null || RUNTIME.equals(key) || RUNTIME_ADM.equals(key)) {
095            return getRuntimeSnapshot();
096        }
097        DistributionSnapshot snap = getPersistentSnapshots(session).get(key);
098        if (snap == null) {
099            DistributionSnapshot rtsnap = getRuntimeSnapshot();
100            if (rtsnap.getKey().equals(key)) {
101                return rtsnap;
102            }
103        }
104        return snap;
105    }
106
107    @Override
108    public List<DistributionSnapshot> readPersistentSnapshots(CoreSession session) {
109        return RepositoryDistributionSnapshot.readPersistentSnapshots(session);
110    }
111
112    @Override
113    public List<DistributionSnapshot> listPersistentSnapshots(CoreSession session) {
114
115        List<DistributionSnapshot> distribs = readPersistentSnapshots(session);
116
117        Collections.sort(distribs,
118                reverseOrder(comparing(DistributionSnapshot::getVersion)).thenComparing(DistributionSnapshot::getName));
119
120        return distribs;
121    }
122
123    @Override
124    public Map<String, DistributionSnapshot> getPersistentSnapshots(CoreSession session) {
125
126        Map<String, DistributionSnapshot> persistentSnapshots = new HashMap<>();
127
128        for (DistributionSnapshot snap : readPersistentSnapshots(session)) {
129            persistentSnapshots.put(snap.getKey(), snap);
130        }
131
132        return persistentSnapshots;
133    }
134
135    @Override
136    public List<String> getPersistentSnapshotNames(CoreSession session) {
137        List<String> names = new ArrayList<>();
138        names.addAll(getPersistentSnapshots(session).keySet());
139        return names;
140    }
141
142    @Override
143    public List<DistributionSnapshotDesc> getAvailableDistributions(CoreSession session) {
144        List<DistributionSnapshotDesc> names = new ArrayList<>();
145        names.addAll(getPersistentSnapshots(session).values());
146        names.add(0, getRuntimeSnapshot());
147        return names;
148    }
149
150    @Override
151    public DistributionSnapshot persistRuntimeSnapshot(CoreSession session) {
152        return persistRuntimeSnapshot(session, null, null);
153    }
154
155    @Override
156    public DistributionSnapshot persistRuntimeSnapshot(CoreSession session, String name,
157            Map<String, Serializable> properties) {
158        return persistRuntimeSnapshot(session, name, properties, null);
159    }
160
161    @Override
162    public DistributionSnapshot persistRuntimeSnapshot(CoreSession session, String name,
163            Map<String, Serializable> properties, SnapshotFilter filter) {
164        DistributionSnapshot liveSnapshot = getRuntimeSnapshot();
165        DistributionSnapshot snap = persister.persist(liveSnapshot, session, name, filter, properties);
166        addPersistentSnapshot(snap.getKey(), snap);
167        return snap;
168    }
169
170    @Override
171    public List<String> getAvailableVersions(CoreSession session, NuxeoArtifact nxItem) {
172        List<String> versions = new ArrayList<>();
173
174        List<DistributionSnapshot> distribs = new ArrayList<>();
175        distribs.addAll(getPersistentSnapshots(session).values());
176        distribs.add(getRuntimeSnapshot());
177
178        for (DistributionSnapshot snap : distribs) {
179
180            String version = null;
181            if (BundleGroup.TYPE_NAME.equals(nxItem.getArtifactType())) {
182                BundleGroup bg = snap.getBundleGroup(nxItem.getId());
183                if (bg != null) {
184                    version = bg.getVersion();
185                }
186            } else if (BundleInfo.TYPE_NAME.equals(nxItem.getArtifactType())) {
187                BundleInfo bi = snap.getBundle(nxItem.getId());
188                if (bi != null) {
189                    version = bi.getVersion();
190                }
191            } else if (ComponentInfo.TYPE_NAME.equals(nxItem.getArtifactType())) {
192                ComponentInfo ci = snap.getComponent(nxItem.getId());
193                if (ci != null) {
194                    version = ci.getVersion();
195                }
196            } else if (ExtensionInfo.TYPE_NAME.equals(nxItem.getArtifactType())) {
197                ExtensionInfo ei = snap.getContribution(nxItem.getId());
198                if (ei != null) {
199                    version = ei.getVersion();
200                }
201            } else if (ExtensionPointInfo.TYPE_NAME.equals(nxItem.getArtifactType())) {
202                ExtensionPointInfo epi = snap.getExtensionPoint(nxItem.getId());
203                if (epi != null) {
204                    version = epi.getVersion();
205                }
206            } else if (ServiceInfo.TYPE_NAME.equals(nxItem.getArtifactType())) {
207                ServiceInfo si = snap.getService(nxItem.getId());
208                if (si != null) {
209                    version = si.getVersion();
210                }
211            } else if (OperationInfo.TYPE_NAME.equals(nxItem.getArtifactType())) {
212                OperationInfo oi = snap.getOperation(nxItem.getId());
213                if (oi != null) {
214                    version = oi.getVersion();
215                }
216            }
217
218            if (version != null && !versions.contains(version)) {
219                versions.add(version);
220            }
221        }
222        return versions;
223    }
224
225    @Override
226    public void exportSnapshot(CoreSession session, String key, OutputStream out) throws IOException {
227
228        DistributionSnapshot snap = getSnapshot(key, session);
229
230        if (snap == null) {
231            throw new NuxeoException("Unable to find Snapshot " + key);
232        }
233
234        if (snap.isLive()) {
235            throw new NuxeoException("Can not export a live distribution snapshot : " + key);
236        }
237
238        RepositoryDistributionSnapshot docSnap = (RepositoryDistributionSnapshot) snap;
239        DocumentModel root = docSnap.getDoc();
240
241        DocumentReader reader = new DocumentTreeReader(session, root);
242        DocumentWriter writer = new NuxeoArchiveWriter(out);
243        DocumentPipe pipe = new DocumentPipeImpl(10);
244        pipe.setReader(reader);
245        pipe.setWriter(writer);
246        pipe.run();
247        reader.close();
248        writer.close();
249    }
250
251    @Override
252    public void importSnapshot(CoreSession session, InputStream is) throws IOException {
253        String importPath = persister.getDistributionRoot(session).getPathAsString();
254        DocumentReader reader = new NuxeoArchiveReader(is);
255        DocumentWriter writer = new DocumentModelWriter(session, importPath);
256        DocumentPipe pipe = new DocumentPipeImpl(10);
257        pipe.setReader(reader);
258        pipe.setWriter(writer);
259        pipe.run();
260        reader.close();
261        writer.close();
262    }
263
264    @Override
265    public void validateImportedSnapshot(CoreSession session, String name, String version, String pathSegment,
266            String title) {
267
268        DocumentModel container = persister.getDistributionRoot(session);
269        DocumentRef tmpRef = new PathRef(container.getPathAsString(), IMPORT_TMP);
270
271        DocumentModel tmp;
272        if (session.exists(tmpRef)) {
273            tmp = session.getChild(container.getRef(), IMPORT_TMP);
274            DocumentModel snapDoc = session.getChildren(tmp.getRef()).get(0);
275            snapDoc.setPropertyValue("nxdistribution:name", name);
276            snapDoc.setPropertyValue("nxdistribution:version", version);
277            snapDoc.setPropertyValue("nxdistribution:key", name + "-" + version);
278            snapDoc.setPropertyValue("dc:title", title);
279            snapDoc = session.saveDocument(snapDoc);
280
281            DocumentModel targetContainer = session.getParentDocument(tmp.getRef());
282
283            session.move(snapDoc.getRef(), targetContainer.getRef(), pathSegment);
284            session.removeDocument(tmp.getRef());
285        }
286
287    }
288
289    @Override
290    public DocumentModel importTmpSnapshot(CoreSession session, InputStream is) throws IOException {
291        DocumentModel container = persister.getDistributionRoot(session);
292        DocumentRef tmpRef = new PathRef(container.getPathAsString(), IMPORT_TMP);
293
294        DocumentModel tmp;
295        if (session.exists(tmpRef)) {
296            tmp = session.getChild(container.getRef(), IMPORT_TMP);
297            session.removeChildren(tmp.getRef());
298        } else {
299            tmp = session.createDocumentModel(container.getPathAsString(), IMPORT_TMP, "Workspace");
300            tmp.setPropertyValue("dc:title", "tmpImport");
301            tmp = session.createDocument(tmp);
302            session.save();
303        }
304
305        DocumentReader reader = new NuxeoArchiveReader(is);
306        DocumentWriter writer = new SnapshotWriter(session, tmp.getPathAsString());
307
308        DocumentPipe pipe = new DocumentPipeImpl(10);
309        pipe.setReader(reader);
310        pipe.setWriter(writer);
311        pipe.run();
312        reader.close();
313        writer.close();
314
315        return session.getChildren(tmp.getRef()).get(0);
316    }
317
318    @Override
319    public void initSeamContext(HttpServletRequest request) {
320        ((RuntimeSnapshot) getRuntimeSnapshot()).initSeamComponents(request);
321    }
322
323    @Override
324    public void addPersistentSnapshot(String key, DistributionSnapshot snapshot) {
325        // NOP
326    }
327
328    /**
329     * Custom Write to disable Validation Service
330     */
331    protected static class SnapshotWriter extends DocumentModelWriter {
332        public SnapshotWriter(CoreSession session, String parentPath) {
333            super(session, parentPath);
334        }
335
336        @Override
337        protected void beforeCreateDocument(DocumentModel doc) {
338            doc.putContextData(CTX_MAP_KEY, TURN_OFF);
339        }
340    }
341
342}