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.Comparator;
028import java.util.HashMap;
029import java.util.List;
030import java.util.Map;
031
032import javax.servlet.http.HttpServletRequest;
033
034import org.apache.commons.logging.Log;
035import org.apache.commons.logging.LogFactory;
036import org.nuxeo.apidoc.api.BundleGroup;
037import org.nuxeo.apidoc.api.BundleInfo;
038import org.nuxeo.apidoc.api.ComponentInfo;
039import org.nuxeo.apidoc.api.ExtensionInfo;
040import org.nuxeo.apidoc.api.ExtensionPointInfo;
041import org.nuxeo.apidoc.api.NuxeoArtifact;
042import org.nuxeo.apidoc.api.OperationInfo;
043import org.nuxeo.apidoc.api.ServiceInfo;
044import org.nuxeo.apidoc.introspection.RuntimeSnapshot;
045import org.nuxeo.apidoc.repository.RepositoryDistributionSnapshot;
046import org.nuxeo.apidoc.repository.SnapshotPersister;
047import org.nuxeo.ecm.core.api.CoreSession;
048import org.nuxeo.ecm.core.api.DocumentModel;
049import org.nuxeo.ecm.core.api.DocumentRef;
050import org.nuxeo.ecm.core.api.NuxeoException;
051import org.nuxeo.ecm.core.api.PathRef;
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
062public class SnapshotManagerComponent extends DefaultComponent implements SnapshotManager {
063
064    protected volatile DistributionSnapshot runtimeSnapshot;
065
066    public static final String RUNTIME = "current";
067
068    public static final String RUNTIME_ADM = "adm";
069
070    protected static final String IMPORT_TMP = "tmpImport";
071
072    protected static final Log log = LogFactory.getLog(SnapshotManagerComponent.class);
073
074    protected final SnapshotPersister persister = new SnapshotPersister();
075
076    @Override
077    public DistributionSnapshot getRuntimeSnapshot() {
078        if (runtimeSnapshot == null) {
079            synchronized (this) {
080                if (runtimeSnapshot == null) {
081                    runtimeSnapshot = RuntimeSnapshot.build();
082                }
083            }
084        }
085        return runtimeSnapshot;
086    }
087
088    @Override
089    public DistributionSnapshot getSnapshot(String key, CoreSession session) {
090        if (key == null || RUNTIME.equals(key) || RUNTIME_ADM.equals(key)) {
091            return getRuntimeSnapshot();
092        }
093        DistributionSnapshot snap = getPersistentSnapshots(session).get(key);
094        if (snap == null) {
095            DistributionSnapshot rtsnap = getRuntimeSnapshot();
096            if (rtsnap.getKey().equals(key)) {
097                return rtsnap;
098            }
099        }
100        return snap;
101    }
102
103    @Override
104    public List<DistributionSnapshot> readPersistentSnapshots(CoreSession session) {
105        List<DistributionSnapshot> snaps = RepositoryDistributionSnapshot.readPersistentSnapshots(session);
106        return snaps;
107    }
108
109    @Override
110    public List<DistributionSnapshot> listPersistentSnapshots(CoreSession session) {
111
112        List<DistributionSnapshot> distribs = readPersistentSnapshots(session);
113
114        Collections.sort(distribs, new Comparator<DistributionSnapshot>() {
115            @Override
116            public int compare(DistributionSnapshot dist0, DistributionSnapshot 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
125        return distribs;
126    }
127
128    @Override
129    public Map<String, DistributionSnapshot> getPersistentSnapshots(CoreSession session) {
130
131        Map<String, DistributionSnapshot> persistentSnapshots = new HashMap<>();
132
133        for (DistributionSnapshot snap : readPersistentSnapshots(session)) {
134            persistentSnapshots.put(snap.getKey(), snap);
135        }
136
137        return persistentSnapshots;
138    }
139
140    @Override
141    public List<String> getPersistentSnapshotNames(CoreSession session) {
142        List<String> names = new ArrayList<>();
143        names.addAll(getPersistentSnapshots(session).keySet());
144        return names;
145    }
146
147    @Override
148    public List<DistributionSnapshotDesc> getAvailableDistributions(CoreSession session) {
149        List<DistributionSnapshotDesc> names = new ArrayList<>();
150        names.addAll(getPersistentSnapshots(session).values());
151        names.add(0, getRuntimeSnapshot());
152        return names;
153    }
154
155    @Override
156    public DistributionSnapshot persistRuntimeSnapshot(CoreSession session) {
157        return persistRuntimeSnapshot(session, null, null);
158    }
159
160    @Override
161    public DistributionSnapshot persistRuntimeSnapshot(CoreSession session, String name, Map<String, Serializable> properties) {
162        return persistRuntimeSnapshot(session, name, properties, null);
163    }
164
165    @Override
166    public DistributionSnapshot persistRuntimeSnapshot(CoreSession session, String name, Map<String, Serializable> properties,
167            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 = null;
276
277        if (session.exists(tmpRef)) {
278            tmp = session.getChild(container.getRef(), IMPORT_TMP);
279            DocumentModel snapDoc = session.getChildren(tmp.getRef()).get(0);
280            snapDoc.setPropertyValue("nxdistribution:name", name);
281            snapDoc.setPropertyValue("nxdistribution:version", version);
282            snapDoc.setPropertyValue("nxdistribution:key", name + "-" + version);
283            snapDoc.setPropertyValue("dc:title", title);
284            snapDoc = session.saveDocument(snapDoc);
285
286            DocumentModel targetContainer = session.getParentDocument(tmp.getRef());
287
288            session.move(snapDoc.getRef(), targetContainer.getRef(), pathSegment);
289            session.removeDocument(tmp.getRef());
290        }
291
292    }
293
294    @Override
295    public DocumentModel importTmpSnapshot(CoreSession session, InputStream is) throws IOException {
296        DocumentModel container = persister.getDistributionRoot(session);
297        DocumentRef tmpRef = new PathRef(container.getPathAsString(), IMPORT_TMP);
298
299        DocumentModel tmp = null;
300
301        if (session.exists(tmpRef)) {
302            tmp = session.getChild(container.getRef(), IMPORT_TMP);
303            session.removeChildren(tmp.getRef());
304        } else {
305            tmp = session.createDocumentModel(container.getPathAsString(), IMPORT_TMP, "Workspace");
306            tmp.setPropertyValue("dc:title", "tmpImport");
307            tmp = session.createDocument(tmp);
308            session.save();
309        }
310
311        DocumentReader reader = new NuxeoArchiveReader(is);
312        DocumentWriter writer = new DocumentModelWriter(session, tmp.getPathAsString());
313
314        DocumentPipe pipe = new DocumentPipeImpl(10);
315        pipe.setReader(reader);
316        pipe.setWriter(writer);
317        pipe.run();
318        reader.close();
319        writer.close();
320
321        return session.getChildren(tmp.getRef()).get(0);
322    }
323
324    @Override
325    public void initSeamContext(HttpServletRequest request) {
326        ((RuntimeSnapshot) getRuntimeSnapshot()).initSeamComponents(request);
327    }
328
329    @Override
330    public void addPersistentSnapshot(String key, DistributionSnapshot snapshot) {
331        // NOP
332    }
333
334}