001/*
002 * (C) Copyright 2006-2014 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 *     Florent Guillaume
018 *     Stephane Lacoin
019 */
020package org.nuxeo.ecm.core.storage;
021
022import java.io.Serializable;
023import java.util.Collection;
024import java.util.Collections;
025import java.util.List;
026
027import org.apache.commons.logging.Log;
028import org.apache.commons.logging.LogFactory;
029import org.nuxeo.ecm.core.api.DocumentModel;
030import org.nuxeo.ecm.core.api.DocumentRef;
031import org.nuxeo.ecm.core.api.IdRef;
032import org.nuxeo.ecm.core.work.AbstractWork;
033import org.nuxeo.ecm.core.work.api.WorkManager;
034
035/**
036 * Work task that inserts the fulltext (extracted manually by the session at save time, or through
037 * FulltextExtractorWork) into the fulltext table.
038 * <p>
039 * This is done single-threaded through the use of a {@link WorkManager} queue with only one thread.
040 */
041public class FulltextUpdaterWork extends AbstractWork {
042
043    private static final long serialVersionUID = 1L;
044
045    private static final Log log = LogFactory.getLog(FulltextUpdaterWork.class);
046
047    public static final String SYSPROP_FULLTEXT_SIMPLE = "fulltextSimple";
048
049    public static final String SYSPROP_FULLTEXT_BINARY = "fulltextBinary";
050
051    public static final String SYSPROP_FULLTEXT_JOBID = "fulltextJobId";
052
053    public static final String FULLTEXT_DEFAULT_INDEX = "default";
054
055    protected static final String CATEGORY = "fulltextUpdater";
056
057    protected static final String TITLE = "Fulltext Updater";
058
059    /** Is this a simple text index or a binary text one. */
060    protected final boolean isSimpleText;
061
062    /** If true, then all the documents with the id as their jobId are updated. */
063    protected final boolean isJob;
064
065    /** The indexes and text to be updated. */
066    protected final List<IndexAndText> indexesAndText;
067
068    public static class IndexAndText implements Serializable {
069        private static final long serialVersionUID = 1L;
070
071        public String indexName;
072
073        public String text;
074
075        public IndexAndText(String indexName, String text) {
076            this.indexName = indexName;
077            this.text = text;
078        }
079    }
080
081    public FulltextUpdaterWork(String repositoryName, String docId, boolean isSimpleText, boolean isJob,
082            List<IndexAndText> indexesAndText) {
083        super(); // random id, for unique job
084        setDocument(repositoryName, docId);
085        this.isSimpleText = isSimpleText;
086        this.isJob = isJob;
087        this.indexesAndText = indexesAndText;
088    }
089
090    @Override
091    public String getCategory() {
092        return CATEGORY;
093    }
094
095    @Override
096    public String getTitle() {
097        return TITLE;
098    }
099
100    @Override
101    public int getRetryCount() {
102        return 1;
103    }
104
105    @Override
106    public void work() {
107        openSystemSession();
108        // if the runtime has shut down (normally because tests are finished)
109        // this can happen, see NXP-4009
110        if (session.getPrincipal() == null) {
111            return;
112        }
113
114        setProgress(Progress.PROGRESS_0_PC);
115        setStatus("Updating");
116        update();
117        setStatus("Saving");
118        session.save();
119        setStatus("Done");
120    }
121
122    protected void update() {
123        Collection<DocumentModel> docs;
124        if (isJob) {
125            String query = String.format("SELECT * FROM Document WHERE ecm:fulltextJobId = '%s' AND ecm:isProxy = 0",
126                    docId);
127            docs = session.query(query);
128        } else {
129            DocumentRef ref = new IdRef(docId);
130            if (!session.exists(ref)) {
131                // doc is gone
132                return;
133            }
134            DocumentModel doc = session.getDocument(ref);
135            if (doc.isProxy()) {
136                // proxies don't have any fulltext attached, it's
137                // the target document that carries it
138                return;
139            }
140            docs = Collections.singleton(doc);
141        }
142        for (DocumentModel doc : docs) {
143            for (IndexAndText indexAndText : indexesAndText) {
144                session.setDocumentSystemProp(doc.getRef(), getFulltextPropertyName(indexAndText.indexName),
145                        indexAndText.text);
146            }
147        }
148        if (isJob) {
149            // reset job id
150            for (DocumentModel doc : docs) {
151                session.setDocumentSystemProp(doc.getRef(), SYSPROP_FULLTEXT_JOBID, null);
152            }
153        }
154    }
155
156    protected String getFulltextPropertyName(String indexName) {
157        String name = isSimpleText ? SYSPROP_FULLTEXT_SIMPLE : SYSPROP_FULLTEXT_BINARY;
158        if (!FULLTEXT_DEFAULT_INDEX.equals(indexName)) {
159            name += '_' + indexName;
160        }
161        return name;
162    }
163
164}