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