001/*
002 * (C) Copyright 2008 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 *     Nuxeo - initial API and implementation
018 *
019 * $Id: IndexingAdapterService.java 31426 2008-04-09 17:00:34Z ogrisel $
020 */
021
022package org.nuxeo.ecm.platform.indexing.gateway.adapter;
023
024import java.util.ArrayList;
025import java.util.Collections;
026import java.util.HashMap;
027import java.util.LinkedList;
028import java.util.List;
029import java.util.Map;
030
031import org.nuxeo.ecm.core.api.CoreSession;
032import org.nuxeo.ecm.platform.api.ws.DocumentBlob;
033import org.nuxeo.ecm.platform.api.ws.DocumentDescriptor;
034import org.nuxeo.ecm.platform.api.ws.DocumentProperty;
035import org.nuxeo.ecm.platform.api.ws.WsACE;
036import org.nuxeo.runtime.RuntimeServiceException;
037import org.nuxeo.runtime.model.ComponentInstance;
038import org.nuxeo.runtime.model.DefaultComponent;
039
040/**
041 * Service to allow client code to register converters for the datastructures served to the Sinequa Intuition to be
042 * indexed. This is especially useful to be able to index Access Control Policy after some post-processing since the ACL
043 * model of Intuition is not as expressive as the Nuxeo Core security model.
044 *
045 * @author Olivier Grisel <ogrisel@nuxeo.com>
046 */
047public class IndexingAdapterService extends DefaultComponent implements IndexingAdapter {
048
049    public static final String INTUITION_ADAPTER_XP = "adapters";
050
051    public static final String BLOB_FORMAT_XP = "blobFormat";
052
053    protected final List<IndexingAdapterDescriptor> registeredAdapters = new LinkedList<IndexingAdapterDescriptor>();
054
055    protected final List<IndexingAdapter> mergedAdapters = new LinkedList<IndexingAdapter>();
056
057    protected boolean useDownloadUrl = true;
058
059    public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
060        if (INTUITION_ADAPTER_XP.equals(extensionPoint)) {
061            mergedAdapters.clear(); // invalidate merged contributions
062            IndexingAdapterDescriptor descriptor = (IndexingAdapterDescriptor) contribution;
063            if (descriptor.isEnabled()) {
064                // do not try to instantiate classes to be disabled by the
065                // contribution
066                IndexingAdapter adapterInstance;
067                try {
068                    adapterInstance = (IndexingAdapter) contributor.getContext().loadClass(descriptor.getClassName()).newInstance();
069                } catch (ReflectiveOperationException e) {
070                    throw new RuntimeException(e);
071                }
072                descriptor.setAdapterInstance(adapterInstance);
073            }
074            registeredAdapters.add(descriptor);
075        } else if (BLOB_FORMAT_XP.equals(extensionPoint)) {
076            BlobFormatDescriptor desc = (BlobFormatDescriptor) contribution;
077            useDownloadUrl = desc.isUseDownloadUrl();
078        } else {
079            throw new RuntimeServiceException("unsupported extension point: " + extensionPoint);
080        }
081    }
082
083    public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
084        if (INTUITION_ADAPTER_XP.equals(extensionPoint)) {
085            mergedAdapters.clear(); // invalidate merged contributions
086            IndexingAdapterDescriptor descriptor = (IndexingAdapterDescriptor) contribution;
087            registeredAdapters.remove(registeredAdapters.lastIndexOf(descriptor));
088
089        } else {
090            throw new RuntimeServiceException("unsupported extension point: " + extensionPoint);
091        }
092    }
093
094    public DocumentDescriptor adaptDocumentDescriptor(CoreSession session, String uuid, DocumentDescriptor dd)
095            {
096        for (IndexingAdapter adapter : getMergedAdapters()) {
097            dd = adapter.adaptDocumentDescriptor(session, uuid, dd);
098        }
099        return dd;
100    }
101
102    public WsACE[] adaptDocumentACL(CoreSession session, String uuid, WsACE[] aces) {
103        for (IndexingAdapter adapter : getMergedAdapters()) {
104            aces = adapter.adaptDocumentACL(session, uuid, aces);
105        }
106        return aces;
107    }
108
109    public WsACE[] adaptDocumentLocalACL(CoreSession session, String uuid, WsACE[] aces) {
110        for (IndexingAdapter adapter : getMergedAdapters()) {
111            aces = adapter.adaptDocumentLocalACL(session, uuid, aces);
112        }
113        return aces;
114    }
115
116    public DocumentBlob[] adaptDocumentBlobs(CoreSession session, String uuid, DocumentBlob[] blobs)
117            {
118        for (IndexingAdapter adapter : getMergedAdapters()) {
119            blobs = adapter.adaptDocumentBlobs(session, uuid, blobs);
120        }
121        return blobs;
122    }
123
124    public DocumentProperty[] adaptDocumentNoBlobProperties(CoreSession session, String uuid,
125            DocumentProperty[] properties) {
126        for (IndexingAdapter adapter : getMergedAdapters()) {
127            properties = adapter.adaptDocumentNoBlobProperties(session, uuid, properties);
128        }
129        return properties;
130    }
131
132    public DocumentProperty[] adaptDocumentProperties(CoreSession session, String uuid, DocumentProperty[] properties)
133            {
134        for (IndexingAdapter adapter : getMergedAdapters()) {
135            properties = adapter.adaptDocumentProperties(session, uuid, properties);
136        }
137        return properties;
138    }
139
140    protected List<IndexingAdapter> getMergedAdapters() {
141        if (mergedAdapters.isEmpty()) {
142            synchronized (this) {
143                Map<String, IndexingAdapterDescriptor> descriptorByClass = new HashMap<String, IndexingAdapterDescriptor>();
144                // merge registered contribution by class names
145                for (IndexingAdapterDescriptor descriptor : registeredAdapters) {
146                    descriptorByClass.put(descriptor.getClassName(), descriptor);
147                }
148
149                // sort merge contributions by order
150                List<IndexingAdapterDescriptor> mergedDescriptors = new ArrayList<IndexingAdapterDescriptor>(
151                        descriptorByClass.values());
152                Collections.sort(mergedDescriptors);
153
154                // filter out disabled adapters and collect the instances of the
155                // remaining sorted contributions
156                for (IndexingAdapterDescriptor descriptor : mergedDescriptors) {
157                    if (descriptor.isEnabled()) {
158                        mergedAdapters.add(descriptor.getAdapterInstance());
159                    }
160                }
161            }
162        }
163        return mergedAdapters;
164    }
165
166    public boolean useDownloadUrlForBlob() {
167        return useDownloadUrl;
168    }
169}