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.repository;
020
021import java.io.Serializable;
022import java.util.ArrayList;
023import java.util.Calendar;
024import java.util.Date;
025import java.util.HashSet;
026import java.util.List;
027import java.util.Map;
028import java.util.Set;
029
030import org.nuxeo.apidoc.adapters.BaseNuxeoArtifactDocAdapter;
031import org.nuxeo.apidoc.api.BundleGroup;
032import org.nuxeo.apidoc.api.BundleInfo;
033import org.nuxeo.apidoc.api.ComponentInfo;
034import org.nuxeo.apidoc.api.ExtensionInfo;
035import org.nuxeo.apidoc.api.ExtensionPointInfo;
036import org.nuxeo.apidoc.api.OperationInfo;
037import org.nuxeo.apidoc.api.QueryHelper;
038import org.nuxeo.apidoc.api.SeamComponentInfo;
039import org.nuxeo.apidoc.api.ServiceInfo;
040import org.nuxeo.apidoc.documentation.JavaDocHelper;
041import org.nuxeo.apidoc.introspection.ServerInfo;
042import org.nuxeo.apidoc.snapshot.DistributionSnapshot;
043import org.nuxeo.common.utils.Path;
044import org.nuxeo.ecm.core.api.CoreSession;
045import org.nuxeo.ecm.core.api.DocumentModel;
046import org.nuxeo.ecm.core.api.DocumentModelList;
047import org.nuxeo.ecm.core.api.DocumentRef;
048import org.nuxeo.ecm.core.api.PathRef;
049import org.nuxeo.ecm.core.api.PropertyException;
050import org.nuxeo.ecm.core.query.sql.NXQL;
051
052public class RepositoryDistributionSnapshot extends BaseNuxeoArtifactDocAdapter implements DistributionSnapshot {
053
054    protected JavaDocHelper jdocHelper = null;
055
056    public static RepositoryDistributionSnapshot create(DistributionSnapshot distrib, CoreSession session,
057            String containerPath, String label, Map<String, Serializable> properties) {
058        DocumentModel doc = session.createDocumentModel(TYPE_NAME);
059        String name = computeDocumentName(distrib.getKey());
060        if (label != null) {
061            name = computeDocumentName(label);
062        }
063        String targetPath = new Path(containerPath).append(name).toString();
064
065        boolean exist = false;
066        if (session.exists(new PathRef(targetPath))) {
067            exist = true;
068            doc = session.getDocument(new PathRef(targetPath));
069        }
070
071        // Set first properties passed by parameter to not override default
072        // behavior
073        if (properties != null) {
074            properties.forEach(doc::setPropertyValue);
075        }
076
077        doc.setPathInfo(containerPath, name);
078        if (label == null) {
079            doc.setPropertyValue("dc:title", distrib.getKey());
080            doc.setPropertyValue(PROP_KEY, distrib.getKey());
081            doc.setPropertyValue(PROP_NAME, distrib.getName());
082        } else {
083            doc.setPropertyValue("dc:title", label);
084            doc.setPropertyValue(PROP_KEY, label + "-" + distrib.getVersion());
085            doc.setPropertyValue(PROP_NAME, label);
086        }
087        doc.setPropertyValue(PROP_LATEST_FT, distrib.isLatestFT());
088        doc.setPropertyValue(PROP_LATEST_LTS, distrib.isLatestLTS());
089        doc.setPropertyValue(PROP_VERSION, distrib.getVersion());
090
091        DocumentModel ret;
092        if (exist) {
093            ret = session.saveDocument(doc);
094        } else {
095            ret = session.createDocument(doc);
096        }
097        return new RepositoryDistributionSnapshot(ret);
098    }
099
100    public static List<DistributionSnapshot> readPersistentSnapshots(CoreSession session) {
101        List<DistributionSnapshot> result = new ArrayList<>();
102        String query = "SELECT * FROM " + TYPE_NAME + " where ecm:currentLifeCycleState != 'deleted'";
103        DocumentModelList docs = session.query(query);
104        for (DocumentModel child : docs) {
105            DistributionSnapshot ob = child.getAdapter(DistributionSnapshot.class);
106            if (ob != null) {
107                result.add(ob);
108            }
109        }
110        return result;
111    }
112
113    public RepositoryDistributionSnapshot(DocumentModel doc) {
114        super(doc);
115    }
116
117    protected <T> List<T> getChildren(Class<T> adapter, String docType) {
118        List<T> result = new ArrayList<>();
119        String query = QueryHelper.select(docType, doc);
120        DocumentModelList docs = getCoreSession().query(query);
121        for (DocumentModel child : docs) {
122            T ob = child.getAdapter(adapter);
123            if (ob != null) {
124                result.add(ob);
125            }
126        }
127        return result;
128    }
129
130    protected <T> T getChild(Class<T> adapter, String docType, String idField, String id) {
131        String query = QueryHelper.select(docType, doc) + " AND " + idField + " = " + NXQL.escapeString(id);
132        DocumentModelList docs = getCoreSession().query(query);
133        if (docs.isEmpty()) {
134            log.error("Unable to find " + docType + " for id " + id);
135        } else if (docs.size() == 1) {
136            return docs.get(0).getAdapter(adapter);
137        } else {
138            log.error("multiple match for " + docType + " for id " + id);
139            return docs.get(0).getAdapter(adapter);
140        }
141        return null;
142    }
143
144    @Override
145    public BundleInfo getBundle(String id) {
146        return getChild(BundleInfo.class, BundleInfo.TYPE_NAME, BundleInfo.PROP_BUNDLE_ID, id);
147    }
148
149    @Override
150    public BundleGroup getBundleGroup(String groupId) {
151        return getChild(BundleGroup.class, BundleGroup.TYPE_NAME, BundleGroup.PROP_KEY, groupId);
152    }
153
154    protected DocumentModel getBundleContainer() {
155        DocumentRef ref = new PathRef(doc.getPathAsString(), SnapshotPersister.Bundle_Root_NAME);
156        if (getCoreSession().exists(ref)) {
157            return getCoreSession().getDocument(ref);
158        } else {
159            // for compatibility with the previous persistence model
160            return doc;
161        }
162    }
163
164    @Override
165    public List<BundleGroup> getBundleGroups() {
166        List<BundleGroup> grps = new ArrayList<>();
167        String query = QueryHelper.select(BundleGroup.TYPE_NAME, doc, NXQL.ECM_PARENTID, getBundleContainer().getId());
168        DocumentModelList docs = getCoreSession().query(query);
169        for (DocumentModel child : docs) {
170            BundleGroup bg = child.getAdapter(BundleGroup.class);
171            if (bg != null) {
172                grps.add(bg);
173            }
174        }
175        return grps;
176    }
177
178    @Override
179    public List<String> getBundleIds() {
180        List<String> ids = new ArrayList<>();
181        for (BundleInfo bi : getChildren(BundleInfo.class, BundleInfo.TYPE_NAME)) {
182            ids.add(bi.getId());
183        }
184        return ids;
185    }
186
187    @Override
188    public ComponentInfo getComponent(String id) {
189        return getChild(ComponentInfo.class, ComponentInfo.TYPE_NAME, ComponentInfo.PROP_COMPONENT_ID, id);
190    }
191
192    @Override
193    public List<String> getComponentIds() {
194        List<String> ids = new ArrayList<>();
195        for (ComponentInfo ci : getChildren(ComponentInfo.class, ComponentInfo.TYPE_NAME)) {
196            ids.add(ci.getId());
197        }
198        return ids;
199    }
200
201    @Override
202    public ExtensionInfo getContribution(String id) {
203        return getChild(ExtensionInfo.class, ExtensionInfo.TYPE_NAME, ExtensionInfo.PROP_CONTRIB_ID, id);
204    }
205
206    @Override
207    public List<String> getContributionIds() {
208        List<String> ids = new ArrayList<>();
209        for (ExtensionInfo xi : getChildren(ExtensionInfo.class, ExtensionInfo.TYPE_NAME)) {
210            ids.add(xi.getId());
211        }
212        return ids;
213    }
214
215    @Override
216    public List<ExtensionInfo> getContributions() {
217        return getChildren(ExtensionInfo.class, ExtensionInfo.TYPE_NAME);
218    }
219
220    @Override
221    public ExtensionPointInfo getExtensionPoint(String id) {
222        return getChild(ExtensionPointInfo.class, ExtensionPointInfo.TYPE_NAME, ExtensionPointInfo.PROP_EP_ID, id);
223    }
224
225    @Override
226    public List<String> getExtensionPointIds() {
227        List<String> ids = new ArrayList<>();
228        for (ExtensionPointInfo xpi : getChildren(ExtensionPointInfo.class, ExtensionPointInfo.TYPE_NAME)) {
229            ids.add(xpi.getId());
230        }
231        return ids;
232    }
233
234    @Override
235    public List<String> getBundleGroupChildren(String groupId) {
236        BundleGroup bg = getChild(BundleGroup.class, BundleGroup.TYPE_NAME, BundleGroup.PROP_KEY, groupId);
237        return bg.getBundleIds();
238    }
239
240    public List<String> getBundleGroupIds() {
241        List<String> ids = new ArrayList<>();
242        for (BundleGroup bg : getChildren(BundleGroup.class, BundleGroup.TYPE_NAME)) {
243            ids.add(bg.getId());
244        }
245        return ids;
246    }
247
248    @Override
249    public List<String> getServiceIds() {
250        Set<String> ids = new HashSet<>();
251        String query = QueryHelper.select(ComponentInfo.TYPE_NAME, doc);
252        DocumentModelList components = getCoreSession().query(query);
253        for (DocumentModel componentDoc : components) {
254            ComponentInfo ci = componentDoc.getAdapter(ComponentInfo.class);
255            if (ci == null) {
256                continue;
257            }
258            ids.addAll(ci.getServiceNames());
259        }
260        return new ArrayList<>(ids);
261    }
262
263    @Override
264    public String getName() {
265        try {
266            return (String) doc.getPropertyValue(PROP_NAME);
267        } catch (PropertyException e) {
268            log.error("Error while reading nxdistribution:name", e);
269            return "!unknown!";
270        }
271    }
272
273    @Override
274    public String getVersion() {
275        try {
276            return (String) doc.getPropertyValue(PROP_VERSION);
277        } catch (PropertyException e) {
278            log.error("Error while reading nxdistribution:version", e);
279            return "!unknown!";
280        }
281    }
282
283    @Override
284    public String getKey() {
285        try {
286            return (String) doc.getPropertyValue(PROP_KEY);
287        } catch (PropertyException e) {
288            log.error("Error while reading nxdistribution:key", e);
289            return "!unknown!";
290        }
291    }
292
293    @Override
294    public List<Class<?>> getSpi() {
295        return null;
296    }
297
298    @Override
299    public String getId() {
300        return getKey();
301    }
302
303    @Override
304    public String getArtifactType() {
305        return TYPE_NAME;
306    }
307
308    @Override
309    public ServiceInfo getService(String id) {
310        String query = QueryHelper.select(ServiceInfo.TYPE_NAME, getDoc()) + " AND " + ServiceInfo.PROP_CLASS_NAME
311                + " = " + NXQL.escapeString(id) + " AND " + ServiceInfo.PROP_OVERRIDEN + " = 0";
312        DocumentModelList docs = getCoreSession().query(query);
313        if (docs.size() > 1) {
314            throw new AssertionError("Multiple services found for " + id);
315        }
316        return docs.get(0).getAdapter(ServiceInfo.class);
317    }
318
319    @Override
320    public List<String> getJavaComponentIds() {
321        List<String> ids = new ArrayList<>();
322        for (ComponentInfo ci : getChildren(ComponentInfo.class, ComponentInfo.TYPE_NAME)) {
323            if (!ci.isXmlPureComponent()) {
324                ids.add(ci.getId());
325            }
326        }
327        return ids;
328    }
329
330    @Override
331    public List<String> getXmlComponentIds() {
332        List<String> ids = new ArrayList<>();
333        for (ComponentInfo ci : getChildren(ComponentInfo.class, ComponentInfo.TYPE_NAME)) {
334            if (ci.isXmlPureComponent()) {
335                ids.add(ci.getId());
336            }
337        }
338        return ids;
339    }
340
341    @Override
342    public Date getCreationDate() {
343        try {
344            Calendar cal = (Calendar) getDoc().getPropertyValue("dc:created");
345            return cal == null ? null : cal.getTime();
346        } catch (PropertyException e) {
347            return null;
348        }
349    }
350
351    @Override
352    public Date getReleaseDate() {
353        try {
354            Calendar cal = (Calendar) getDoc().getPropertyValue("nxdistribution:released");
355            return cal == null ? getCreationDate() : cal.getTime();
356        } catch (PropertyException e) {
357            return null;
358        }
359    }
360
361    @Override
362    public boolean isLive() {
363        return false;
364    }
365
366    @Override
367    public SeamComponentInfo getSeamComponent(String id) {
368        String name = id.replace("seam:", "");
369        String query = QueryHelper.select(SeamComponentInfo.TYPE_NAME, getDoc()) + " AND "
370                + SeamComponentInfo.PROP_COMPONENT_NAME + " = " + NXQL.escapeString(name);
371        DocumentModelList docs = getCoreSession().query(query);
372        return docs.isEmpty() ? null : docs.get(0).getAdapter(SeamComponentInfo.class);
373    }
374
375    @Override
376    public List<String> getSeamComponentIds() {
377        List<String> result = new ArrayList<>();
378        String query = QueryHelper.select(SeamComponentInfo.TYPE_NAME, getDoc());
379        DocumentModelList docs = getCoreSession().query(query);
380        for (DocumentModel doc : docs) {
381            result.add(doc.getAdapter(SeamComponentInfo.class).getId());
382        }
383        return result;
384    }
385
386    @Override
387    public List<SeamComponentInfo> getSeamComponents() {
388        List<SeamComponentInfo> result = new ArrayList<>();
389        String query = QueryHelper.select(SeamComponentInfo.TYPE_NAME, getDoc());
390        DocumentModelList docs = getCoreSession().query(query);
391        for (DocumentModel doc : docs) {
392            result.add(doc.getAdapter(SeamComponentInfo.class));
393        }
394        return result;
395    }
396
397    @Override
398    public boolean containsSeamComponents() {
399        return getSeamComponentIds().size() > 0;
400    }
401
402    @Override
403    public OperationInfo getOperation(String id) {
404        if (id.startsWith(OperationInfo.ARTIFACT_PREFIX)) {
405            id = id.substring(OperationInfo.ARTIFACT_PREFIX.length());
406        }
407        String query = QueryHelper.select(OperationInfo.TYPE_NAME, getDoc()) + " AND " + OperationInfo.PROP_NAME
408                + " = " + NXQL.escapeString(id);
409        DocumentModelList docs = getCoreSession().query(query);
410        return docs.isEmpty() ? null : docs.get(0).getAdapter(OperationInfo.class);
411    }
412
413    @Override
414    public List<OperationInfo> getOperations() {
415        List<OperationInfo> result = new ArrayList<>();
416        String query = QueryHelper.select(OperationInfo.TYPE_NAME, getDoc());
417        DocumentModelList docs = getCoreSession().query(query);
418        for (DocumentModel doc : docs) {
419            result.add(doc.getAdapter(OperationInfo.class));
420        }
421        // TODO sort
422        return result;
423    }
424
425    public JavaDocHelper getJavaDocHelper() {
426        if (jdocHelper == null) {
427            jdocHelper = JavaDocHelper.getHelper(getName(), getVersion());
428        }
429        return jdocHelper;
430    }
431
432    @Override
433    public void cleanPreviousArtifacts() {
434        String query = QueryHelper.select("Document", getDoc());
435        List<DocumentRef> refs = new ArrayList<>();
436        DocumentModelList docs = getCoreSession().query(query);
437        for (DocumentModel doc : docs) {
438            refs.add(doc.getRef());
439        }
440        getCoreSession().removeDocuments(refs.toArray(new DocumentRef[refs.size()]));
441    }
442
443    @Override
444    public boolean isLatestFT() {
445        try {
446            return (Boolean) doc.getPropertyValue(PROP_LATEST_FT);
447        } catch (PropertyException e) {
448            log.error("Error while reading nxdistribution:latestFT", e);
449            return false;
450        }
451    }
452
453    @Override
454    public boolean isLatestLTS() {
455        try {
456            return (Boolean) doc.getPropertyValue(PROP_LATEST_LTS);
457        } catch (PropertyException e) {
458            log.error("Error while reading nxdistribution:latestLTS", e);
459            return false;
460        }
461    }
462
463    @Override
464    public List<String> getAliases() {
465        @SuppressWarnings("unchecked")
466        List<String> aliases = (List<String>) doc.getPropertyValue(PROP_ALIASES);
467        if (isLatestLTS()) {
468            aliases.add("latestLTS");
469        } else if (isLatestFT()) {
470            aliases.add("latestFT");
471        }
472        return aliases;
473    }
474
475    @Override
476    public boolean isHidden() {
477        return Boolean.TRUE.equals(doc.getPropertyValue(PROP_HIDE));
478    }
479
480    @Override
481    public ServerInfo getServerInfo() {
482        throw new UnsupportedOperationException();
483    }
484}