001/*
002 * (C) Copyright 2006-2010 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Thierry Delprat
016 */
017package org.nuxeo.apidoc.introspection;
018
019import java.util.ArrayList;
020import java.util.Collections;
021import java.util.Date;
022import java.util.HashMap;
023import java.util.List;
024import java.util.Map;
025
026import javax.servlet.http.HttpServletRequest;
027
028import org.nuxeo.apidoc.api.BaseNuxeoArtifact;
029import org.nuxeo.apidoc.api.BundleGroup;
030import org.nuxeo.apidoc.api.BundleGroupFlatTree;
031import org.nuxeo.apidoc.api.BundleGroupTreeHelper;
032import org.nuxeo.apidoc.api.ComponentInfo;
033import org.nuxeo.apidoc.api.ExtensionInfo;
034import org.nuxeo.apidoc.api.ExtensionPointInfo;
035import org.nuxeo.apidoc.api.OperationInfo;
036import org.nuxeo.apidoc.api.SeamComponentInfo;
037import org.nuxeo.apidoc.api.ServiceInfo;
038import org.nuxeo.apidoc.documentation.JavaDocHelper;
039import org.nuxeo.apidoc.seam.SeamRuntimeIntrospector;
040import org.nuxeo.apidoc.snapshot.DistributionSnapshot;
041import org.nuxeo.ecm.automation.AutomationService;
042import org.nuxeo.ecm.automation.OperationDocumentation;
043import org.nuxeo.ecm.automation.OperationException;
044import org.nuxeo.ecm.automation.OperationType;
045import org.nuxeo.ecm.core.api.NuxeoException;
046import org.nuxeo.runtime.api.Framework;
047
048public class RuntimeSnapshot extends BaseNuxeoArtifact implements DistributionSnapshot {
049
050    public static final String VIRTUAL_BUNDLE_GROUP = "grp:org.nuxeo.misc";
051
052    protected ServerInfo serverInfo;
053
054    protected Date created;
055
056    protected final List<String> bundlesIds = new ArrayList<String>();
057
058    protected final List<String> javaComponentsIds = new ArrayList<String>();
059
060    protected final Map<String, String> components2Bundles = new HashMap<String, String>();
061
062    protected final Map<String, String> services2Components = new HashMap<String, String>();
063
064    protected final Map<String, ExtensionPointInfo> extensionPoints = new HashMap<String, ExtensionPointInfo>();
065
066    protected final Map<String, ExtensionInfo> contributions = new HashMap<String, ExtensionInfo>();
067
068    protected final Map<String, List<String>> mavenGroups = new HashMap<String, List<String>>();
069
070    protected final Map<String, List<String>> mavenSubGroups = new HashMap<String, List<String>>();
071
072    protected final List<BundleGroup> bundleGroups = new ArrayList<BundleGroup>();
073
074    protected boolean seamInitialized = false;
075
076    protected List<SeamComponentInfo> seamComponents = new ArrayList<SeamComponentInfo>();
077
078    protected boolean opsInitialized = false;
079
080    protected final List<OperationInfo> operations = new ArrayList<OperationInfo>();
081
082    protected JavaDocHelper jdocHelper;
083
084    protected final List<Class<?>> spi = new ArrayList<Class<?>>();
085
086    public RuntimeSnapshot() {
087        buildServerInfo();
088    }
089
090    @Override
091    public String getVersion() {
092        return serverInfo.getVersion();
093    }
094
095    @Override
096    public String getName() {
097        return serverInfo.getName();
098    }
099
100    protected synchronized ServerInfo buildServerInfo() {
101        if (serverInfo == null) {
102            serverInfo = ServerInfo.build();
103            created = new Date();
104            spi.addAll(serverInfo.getAllSpi());
105            for (BundleInfoImpl bInfo : serverInfo.getBundles()) {
106                bundlesIds.add(bInfo.getId());
107
108                String groupId = bInfo.getArtifactGroupId();
109                if (groupId != null) {
110                    groupId = "grp:" + groupId;
111                }
112                String artifactId = bInfo.getArtifactId();
113
114                if (groupId == null || artifactId == null) {
115                    groupId = VIRTUAL_BUNDLE_GROUP;
116                    bInfo.setGroupId(groupId);
117                }
118                if (!mavenGroups.containsKey(groupId)) {
119                    mavenGroups.put(groupId, new ArrayList<String>());
120                }
121                mavenGroups.get(groupId).add(bInfo.getId());
122
123                for (ComponentInfo cInfo : bInfo.getComponents()) {
124                    components2Bundles.put(cInfo.getId(), bInfo.getId());
125                    if (!cInfo.isXmlPureComponent()) {
126                        javaComponentsIds.add(cInfo.getId());
127                    }
128
129                    for (String serviceName : cInfo.getServiceNames()) {
130                        services2Components.put(serviceName, cInfo.getId());
131                    }
132
133                    for (ExtensionPointInfo epi : cInfo.getExtensionPoints()) {
134                        extensionPoints.put(epi.getId(), epi);
135                    }
136
137                    for (ExtensionInfo ei : cInfo.getExtensions()) {
138                        contributions.put(ei.getId(), ei);
139                    }
140                }
141            }
142        }
143
144        // post process bundle groups
145        List<String> mvnGroupNames = new ArrayList<String>();
146        mvnGroupNames.addAll(mavenGroups.keySet());
147
148        for (String mvnGroupName : mvnGroupNames) {
149            List<String> artifactIds = mavenGroups.get(mvnGroupName);
150            Collections.sort(artifactIds);
151
152            List<String> subGroups = new ArrayList<String>();
153
154            for (String id : artifactIds) {
155                if (id.endsWith(".api")) {
156                    String grp = "grp:" + id.substring(0, id.length() - 4);
157
158                    if (grp.equals(mvnGroupName)) {
159                        continue;
160                    }
161
162                    subGroups.add(grp);
163                }
164            }
165
166            if (subGroups.size() < 2) {
167                // no need to split the maven group into subGroups
168            } else {
169                for (String grp : subGroups) {
170                    List<String> grpArtifactIds = new ArrayList<String>();
171                    for (String aid : artifactIds) {
172                        if (aid.startsWith(grp) || ("grp:" + aid).startsWith(grp)) {
173                            grpArtifactIds.add(aid);
174                        }
175                    }
176                    if (grpArtifactIds.size() > 0) {
177                        for (String aid : grpArtifactIds) {
178                            artifactIds.remove(aid);
179                        }
180                        mavenSubGroups.put(grp, grpArtifactIds);
181                        artifactIds.add(grp);
182                    }
183                }
184            }
185        }
186
187        for (String grpId : mavenGroups.keySet()) {
188            BundleGroupImpl bGroup = buildBundleGroup(grpId, serverInfo.getVersion());
189            bundleGroups.add(bGroup);
190        }
191        return serverInfo;
192    }
193
194    protected BundleGroupImpl buildBundleGroup(String id, String version) {
195        BundleGroupImpl bGroup = new BundleGroupImpl(id, version);
196        for (String aid : getBundleGroupChildren(id)) {
197            if (aid.startsWith("grp:")) {
198                BundleGroupImpl newGroup = buildBundleGroup(aid, version);
199                bGroup.add(newGroup);
200                newGroup.addParent(bGroup.getId());
201            } else {
202                bGroup.add(aid);
203                getBundle(aid).setBundleGroup(bGroup);
204                BundleInfoImpl bi = getBundle(aid);
205                bGroup.addLiveDoc(bi.getParentLiveDoc());
206            }
207        }
208        return bGroup;
209    }
210
211    @Override
212    public List<BundleGroup> getBundleGroups() {
213        return bundleGroups;
214    }
215
216    @Override
217    public BundleGroup getBundleGroup(String groupId) {
218        BundleGroupTreeHelper bgth = new BundleGroupTreeHelper(this);
219        List<BundleGroupFlatTree> tree = bgth.getBundleGroupTree();
220
221        for (BundleGroupFlatTree info : tree) {
222            if (info.getGroup().getId().equals(groupId)) {
223                return info.getGroup();
224            }
225        }
226        if (!groupId.startsWith("grp:")) {
227            return getBundleGroup("grp:" + groupId);
228        }
229        return null;
230    }
231
232    protected void browseBundleGroup(BundleGroup group, int level, List<BundleGroupFlatTree> tree) {
233        BundleGroupFlatTree info = new BundleGroupFlatTree(group, level);
234        tree.add(info);
235
236        for (BundleGroup subGroup : group.getSubGroups()) {
237            browseBundleGroup(subGroup, level + 1, tree);
238        }
239    }
240
241    @Override
242    public List<String> getBundleIds() {
243        List<String> bundlesIds = new ArrayList<String>();
244
245        for (BundleInfoImpl info : serverInfo.getBundles()) {
246            bundlesIds.add(info.getId());
247
248        }
249        Collections.sort(bundlesIds);
250        return bundlesIds;
251    }
252
253    @Override
254    public BundleInfoImpl getBundle(String id) {
255        return serverInfo.getBundle(id);
256    }
257
258    @Override
259    public List<String> getComponentIds() {
260        List<String> componentsIds = new ArrayList<String>();
261        componentsIds.addAll(components2Bundles.keySet());
262        Collections.sort(componentsIds);
263        return componentsIds;
264    }
265
266    @Override
267    public ComponentInfo getComponent(String id) {
268        String bundleId = components2Bundles.get(id);
269        if (bundleId == null) {
270            return null;
271        }
272        BundleInfoImpl bi = getBundle(bundleId);
273
274        for (ComponentInfo ci : bi.getComponents()) {
275            if (ci.getId().equals(id)) {
276                return ci;
277            }
278        }
279        return null;
280    }
281
282    @Override
283    public List<String> getServiceIds() {
284        List<String> serviceIds = new ArrayList<String>();
285        serviceIds.addAll(services2Components.keySet());
286        Collections.sort(serviceIds);
287        return serviceIds;
288    }
289
290    @Override
291    public List<String> getExtensionPointIds() {
292        List<String> epIds = new ArrayList<String>();
293        epIds.addAll(extensionPoints.keySet());
294        Collections.sort(epIds);
295        return epIds;
296    }
297
298    @Override
299    public ExtensionPointInfo getExtensionPoint(String id) {
300        return extensionPoints.get(id);
301    }
302
303    @Override
304    public List<String> getContributionIds() {
305        List<String> contribIds = new ArrayList<String>();
306        contribIds.addAll(contributions.keySet());
307        Collections.sort(contribIds);
308        return contribIds;
309    }
310
311    @Override
312    public List<ExtensionInfo> getContributions() {
313        List<ExtensionInfo> contribs = new ArrayList<ExtensionInfo>();
314        contribs.addAll(contributions.values());
315        // TODO sort
316        return contribs;
317    }
318
319    @Override
320    public ExtensionInfo getContribution(String id) {
321        return contributions.get(id);
322    }
323
324    public List<String> getBundleGroupIds() {
325        List<String> grpIds = new ArrayList<String>();
326        grpIds.addAll(mavenGroups.keySet());
327        Collections.sort(grpIds);
328        return grpIds;
329    }
330
331    @Override
332    public List<String> getBundleGroupChildren(String groupId) {
333        List<String> res = mavenSubGroups.get(groupId);
334        if (res == null) {
335            // String grpId = groupId.substring(4);
336            res = mavenGroups.get(groupId);
337        }
338
339        if (res != null) {
340            return res;
341        } else {
342            return new ArrayList<String>();
343        }
344    }
345
346    @Override
347    public String getKey() {
348        return getName() + "-" + getVersion();
349    }
350
351    @Override
352    public List<Class<?>> getSpi() {
353        return spi;
354    }
355
356    @Override
357    public String getId() {
358        return getKey();
359    }
360
361    @Override
362    public String getArtifactType() {
363        return TYPE_NAME;
364    }
365
366    @Override
367    public ServiceInfo getService(String id) {
368        String cId = services2Components.get(id);
369        if (cId == null) {
370            return null;
371        }
372
373        for (ServiceInfo si : getComponent(cId).getServices()) {
374            if (id.equals(si.getId())) {
375                return si;
376            }
377        }
378        return null;
379    }
380
381    @Override
382    public List<String> getJavaComponentIds() {
383        return javaComponentsIds;
384    }
385
386    @Override
387    public List<String> getXmlComponentIds() {
388        List<String> result = new ArrayList<String>();
389
390        for (String cId : getComponentIds()) {
391            if (!javaComponentsIds.contains(cId)) {
392                result.add(cId);
393            }
394        }
395        return result;
396    }
397
398    @Override
399    public Date getCreationDate() {
400        return created;
401    }
402
403    @Override
404    public boolean isLive() {
405        return true;
406    }
407
408    @Override
409    public String getHierarchyPath() {
410        // TODO Auto-generated method stub
411        return null;
412    }
413
414    public void initSeamComponents(HttpServletRequest request) {
415        if (seamInitialized) {
416            return;
417        }
418        seamComponents = SeamRuntimeIntrospector.listNuxeoComponents(request);
419        for (SeamComponentInfo seamComp : seamComponents) {
420            ((SeamComponentInfoImpl) seamComp).setVersion(getVersion());
421        }
422        seamInitialized = true;
423    }
424
425    public void initOperations() {
426        if (opsInitialized) {
427            return;
428        }
429        OperationType[] ops = Framework.getService(AutomationService.class).getOperations();
430        for (OperationType op : ops) {
431            OperationDocumentation documentation;
432            try {
433                documentation = op.getDocumentation();
434            } catch (OperationException e) {
435                throw new NuxeoException(e);
436            }
437            operations.add(new OperationInfoImpl(documentation, getVersion(), op.getType().getCanonicalName(),
438                    op.getContributingComponent()));
439        }
440        opsInitialized = true;
441    }
442
443    @Override
444    public SeamComponentInfo getSeamComponent(String id) {
445        for (SeamComponentInfo sci : getSeamComponents()) {
446            if (sci.getId().equals(id)) {
447                return sci;
448            }
449        }
450        return null;
451    }
452
453    @Override
454    public List<String> getSeamComponentIds() {
455        List<String> ids = new ArrayList<String>();
456        for (SeamComponentInfo sci : getSeamComponents()) {
457            ids.add(sci.getId());
458        }
459        return ids;
460    }
461
462    @Override
463    public List<SeamComponentInfo> getSeamComponents() {
464        return seamComponents;
465    }
466
467    @Override
468    public boolean containsSeamComponents() {
469        return seamInitialized && getSeamComponentIds().size() > 0;
470    }
471
472    @Override
473    public OperationInfo getOperation(String id) {
474        if (id.startsWith(OperationInfo.ARTIFACT_PREFIX)) {
475            id = id.substring(OperationInfo.ARTIFACT_PREFIX.length());
476        }
477        for (OperationInfo op : getOperations()) {
478            if (op.getName().equals(id)) {
479                return op;
480            }
481        }
482        return null;
483    }
484
485    @Override
486    public List<OperationInfo> getOperations() {
487        initOperations();
488        return operations;
489    }
490
491    @Override
492    public JavaDocHelper getJavaDocHelper() {
493        if (jdocHelper == null) {
494            jdocHelper = JavaDocHelper.getHelper(getName(), getVersion());
495        }
496        return jdocHelper;
497    }
498
499    @Override
500    public void cleanPreviousArtifacts() {
501        // Can't delete anything in a runtime Snapshot
502        throw new UnsupportedOperationException();
503    }
504}