001/*
002 * (C) Copyright 2013 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 *     dmetzler
018 */
019package org.nuxeo.ecm.automation.io.services.enricher;
020
021import java.io.IOException;
022import java.util.ArrayList;
023import java.util.Arrays;
024import java.util.Collections;
025import java.util.HashSet;
026import java.util.List;
027import java.util.Map;
028import java.util.Map.Entry;
029import java.util.Set;
030import java.util.concurrent.ConcurrentHashMap;
031
032import javax.ws.rs.core.HttpHeaders;
033
034import org.apache.commons.logging.Log;
035import org.apache.commons.logging.LogFactory;
036import org.codehaus.jackson.JsonGenerationException;
037import org.codehaus.jackson.JsonGenerator;
038import org.jboss.el.ExpressionFactoryImpl;
039import org.nuxeo.common.utils.StringUtils;
040import org.nuxeo.ecm.core.api.CoreSession;
041import org.nuxeo.ecm.core.api.NuxeoPrincipal;
042import org.nuxeo.ecm.core.io.marshallers.json.enrichers.AbstractJsonEnricher;
043import org.nuxeo.ecm.platform.actions.ActionContext;
044import org.nuxeo.ecm.platform.actions.ELActionContext;
045import org.nuxeo.ecm.platform.actions.ejb.ActionManager;
046import org.nuxeo.ecm.platform.el.ExpressionContext;
047import org.nuxeo.runtime.api.Framework;
048import org.nuxeo.runtime.model.ComponentContext;
049import org.nuxeo.runtime.model.ComponentInstance;
050import org.nuxeo.runtime.model.DefaultComponent;
051
052/**
053 * @since 5.7.3
054 * @deprecated The JSON marshalling was migrated to nuxeo-core-io. An enricher system is also available. See
055 *             org.nuxeo.ecm.core.io.marshallers.json.enrichers.BreadcrumbJsonEnricher for an example. To migrate an
056 *             existing enricher, keep the marshalling code and use it in class implementing
057 *             AbstractJsonEnricher<DocumentModel> (the use of contextual parameters is a bit different but
058 *             compatible / you have to manage the enricher's parameters yourself). Don't forget to contribute to
059 *             service org.nuxeo.ecm.core.io.registry.MarshallerRegistry to register your enricher.
060 */
061@Deprecated
062public class ContentEnricherServiceImpl extends DefaultComponent implements ContentEnricherService {
063
064    /**
065    *
066    */
067    public static final String NXCONTENT_CATEGORY_HEADER = "X-NXContext-Category";
068
069    protected static final Log log = LogFactory.getLog(ContentEnricherServiceImpl.class);
070
071    public static final String ENRICHER = "enricher";
072
073    public static List<Class<?>> DEPRECATED_KNOWN_ENRICHERS = Arrays.asList(ACLContentEnricher.class,
074            PreviewContentEnricher.class, ThumbnailContentEnricher.class, UserPermissionsContentEnricher.class);
075
076    private Map<String, ContentEnricherDescriptor> descriptorRegistry = new ConcurrentHashMap<>();
077
078    private Set<Class<?>> customEnrichers = Collections.synchronizedSet(new HashSet<>());
079
080    @Override
081    public void activate(ComponentContext context) {
082        super.activate(context);
083        List<String> customDeprecated = new ArrayList<>();
084        List<Class<?>> deprecatedKnown = Arrays.asList(ACLContentEnricher.class, PreviewContentEnricher.class,
085                ThumbnailContentEnricher.class, UserPermissionsContentEnricher.class);
086        for (ContentEnricherDescriptor descriptor : descriptorRegistry.values()) {
087            if (!deprecatedKnown.contains(descriptor.klass)) {
088                customDeprecated.add(descriptor.klass.getSimpleName());
089            }
090        }
091        if (!customDeprecated.isEmpty()) {
092            log.warn("The ContentEnricherService is deprecated since 7.10. The contributed enrichers are still available through the deprecated JAX-RS document's marshallers but won't be available through the REST API or automation. You should migrate the following classes and implement "
093                    + AbstractJsonEnricher.class.getName() + ": " + StringUtils.join(customDeprecated, ','));
094        }
095    }
096
097    @Override
098    public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
099
100        if (ENRICHER.equals(extensionPoint)) {
101            ContentEnricherDescriptor cd = (ContentEnricherDescriptor) contribution;
102            if (!DEPRECATED_KNOWN_ENRICHERS.contains(cd.klass)) {
103                if (customEnrichers.isEmpty()) {
104                    log.warn("The ContentEnricherService is deprecated since 7.10. The contributed enrichers are still available for use through the deprecated JAX-RS document's marshallers but won't be available through the default Nuxeo REST API or Nuxeo Automation. You should migrate your enrichers and implement "
105                            + AbstractJsonEnricher.class.getName());
106                }
107                if (!customEnrichers.contains(cd.klass)) {
108                    customEnrichers.add(cd.klass);
109                    log.warn("Enrichers registered and not available for use in Nuxeo Rest API and Nuxeo Automation: "
110                            + cd.klass.getName());
111                }
112            }
113            descriptorRegistry.put(cd.name, cd);
114        }
115
116    }
117
118    @Override
119    public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
120        if (ENRICHER.equals(extensionPoint)) {
121            ContentEnricherDescriptor cd = (ContentEnricherDescriptor) contribution;
122            if (descriptorRegistry.containsKey(cd.name)) {
123                descriptorRegistry.remove(cd.name);
124                if (!DEPRECATED_KNOWN_ENRICHERS.contains(cd.klass)) {
125                    customEnrichers.remove(cd.name);
126                }
127            }
128        }
129    }
130
131    @Override
132    public List<ContentEnricher> getEnrichers(String category, RestEvaluationContext context) {
133        List<ContentEnricher> result = new ArrayList<>();
134        for (ContentEnricherDescriptor descriptor : getEnricherDescriptors(category, context)) {
135
136            ContentEnricher contentEnricher = descriptor.getContentEnricher();
137            result.add(contentEnricher);
138        }
139
140        return result;
141    }
142
143    private List<ContentEnricherDescriptor> getEnricherDescriptors(String category, RestEvaluationContext context) {
144        List<ContentEnricherDescriptor> result = new ArrayList<>();
145        for (Entry<String, ContentEnricherDescriptor> entry : descriptorRegistry.entrySet()) {
146            ContentEnricherDescriptor descriptor = entry.getValue();
147            if (descriptor.categories.contains(category)) {
148                result.add(descriptor);
149            }
150        }
151        return result;
152    }
153
154    @Override
155    public void writeContext(JsonGenerator jg, RestEvaluationContext ec) throws JsonGenerationException, IOException {
156
157        for (String category : getCategoriesToActivate(ec)) {
158            for (ContentEnricherDescriptor descriptor : getEnricherDescriptors(category, ec)) {
159                if (evaluateFilter(ec, descriptor)) {
160                    ContentEnricher enricher = descriptor.getContentEnricher();
161                    if (enricher != null) {
162                        jg.writeFieldName(descriptor.name);
163                        enricher.enrich(jg, ec);
164                    }
165                }
166            }
167        }
168
169    }
170
171    /**
172     * @param ec
173     * @param descriptor
174     * @return
175     */
176    private boolean evaluateFilter(RestEvaluationContext ec, ContentEnricherDescriptor descriptor) {
177        for (String filterId : descriptor.filterIds) {
178            ActionManager as = Framework.getLocalService(ActionManager.class);
179            if (!as.checkFilter(filterId, createActionContext(ec))) {
180                return false;
181            }
182        }
183        return true;
184    }
185
186    /**
187     * Creates an ActionService compatible ActionContext to evaluate filters
188     *
189     * @param ec
190     * @return
191     */
192    private ActionContext createActionContext(RestEvaluationContext ec) {
193        ActionContext actionContext = new ELActionContext(new ExpressionContext(), new ExpressionFactoryImpl());
194        CoreSession session = ec.getDocumentModel().getCoreSession();
195        actionContext.setDocumentManager(session);
196        actionContext.setCurrentPrincipal((NuxeoPrincipal) session.getPrincipal());
197
198        actionContext.setCurrentDocument(ec.getDocumentModel());
199
200        return actionContext;
201    }
202
203    private List<String> getCategoriesToActivate(RestEvaluationContext ec) {
204        HttpHeaders headers = ec.getHeaders();
205        if (headers != null) {
206            List<String> requestHeader = headers.getRequestHeader(NXCONTENT_CATEGORY_HEADER);
207            if (requestHeader != null && !requestHeader.isEmpty()) {
208                return Arrays.asList(StringUtils.split(requestHeader.get(0), ',', true));
209            } else {
210                return new ArrayList<>(0);
211            }
212        }
213        return new ArrayList<>(0);
214    }
215
216}