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