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