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