001/*
002 * (C) Copyright 2015 Nuxeo SA (http://nuxeo.com/) and others.
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 *     Thomas Roger
016 */
017
018package org.nuxeo.ecm.core.security;
019
020import static org.nuxeo.ecm.core.api.event.CoreEventConstants.DOCUMENT_REFS;
021import static org.nuxeo.ecm.core.api.event.CoreEventConstants.REPOSITORY_NAME;
022import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.ACE_STATUS_UPDATED;
023
024import java.io.Serializable;
025import java.text.SimpleDateFormat;
026import java.util.ArrayList;
027import java.util.Date;
028import java.util.List;
029import java.util.Map;
030
031import org.nuxeo.ecm.core.api.DocumentRef;
032import org.nuxeo.ecm.core.api.IdRef;
033import org.nuxeo.ecm.core.api.IterableQueryResult;
034import org.nuxeo.ecm.core.api.security.ACP;
035import org.nuxeo.ecm.core.event.EventContext;
036import org.nuxeo.ecm.core.event.EventService;
037import org.nuxeo.ecm.core.event.impl.EventContextImpl;
038import org.nuxeo.ecm.core.query.sql.NXQL;
039import org.nuxeo.ecm.core.work.AbstractWork;
040import org.nuxeo.runtime.api.Framework;
041import org.nuxeo.runtime.transaction.TransactionRuntimeException;
042
043/**
044 * Work updating ACE status.
045 *
046 * @since 7.4
047 */
048public class UpdateACEStatusWork extends AbstractWork {
049
050    public static final int DEFAULT_BATCH_SIZE = 20;
051
052    public static final String ID = "updateACEStatus";
053
054    public static final String CATEGORY = "updateACEStatus";
055
056    public static final String QUERY = "SELECT ecm:uuid FROM Document WHERE (ecm:acl/*1/status = 0 AND ecm:acl/*1/begin <= TIMESTAMP '%s') OR (ecm:acl/*1/status = 1 AND ecm:acl/*1/end <= TIMESTAMP '%s')";
057
058    public static final SimpleDateFormat FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
059
060    protected int batchSize = DEFAULT_BATCH_SIZE;
061
062    public UpdateACEStatusWork() {
063        super(ID);
064    }
065
066    @Override
067    public void work() {
068        setStatus("Updating ACE status");
069        initSession();
070
071        Date now = new Date();
072        String formattedDate = FORMATTER.format(now);
073
074        IterableQueryResult result = session.queryAndFetch(String.format(QUERY, formattedDate, formattedDate),
075                NXQL.NXQL);
076        List<String> docIds = new ArrayList<>();
077        try {
078            for (Map<String, Serializable> map : result) {
079                docIds.add((String) map.get("ecm:uuid"));
080            }
081        } finally {
082            result.close();
083        }
084
085        int acpUpdatedCount = 0;
086        List<DocumentRef> processedDocIds = new ArrayList<>();
087        for (String docId : docIds) {
088            try {
089                DocumentRef ref = new IdRef(docId);
090                ACP acp = session.getACP(ref);
091                session.setACP(ref, acp, true);
092                acpUpdatedCount++;
093                processedDocIds.add(ref);
094                if (acpUpdatedCount % batchSize == 0) {
095                    fireACEStatusUpdatedEvent(processedDocIds);
096                    commitOrRollbackTransaction();
097                    startTransaction();
098                    processedDocIds.clear();
099                }
100            } catch (TransactionRuntimeException e) {
101                if (e.getMessage().contains("Transaction timeout")) {
102                    batchSize = 1;
103                }
104                throw e;
105            }
106        }
107        fireACEStatusUpdatedEvent(processedDocIds);
108
109        setStatus(null);
110    }
111
112    protected void fireACEStatusUpdatedEvent(List<DocumentRef> docRefs) {
113        EventContext eventContext = new EventContextImpl(session, session.getPrincipal());
114        eventContext.setProperty(DOCUMENT_REFS, (Serializable) docRefs);
115        eventContext.setProperty(REPOSITORY_NAME, session.getRepositoryName());
116        Framework.getService(EventService.class).fireEvent(ACE_STATUS_UPDATED, eventContext);
117    }
118
119    @Override
120    public String getCategory() {
121        return CATEGORY;
122    }
123
124    @Override
125    public String getTitle() {
126        return "Updating ACE status";
127    }
128
129    @Override
130    public int getRetryCount() {
131        return 10;
132    }
133}