001/*
002 * (C) Copyright 2009 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.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 *     Anahide Tchertchian
016 */
017package org.nuxeo.ecm.platform.error.web;
018
019import static org.jboss.seam.ScopeType.EVENT;
020
021import java.io.Serializable;
022import java.util.Collections;
023import java.util.HashMap;
024import java.util.List;
025import java.util.Map;
026
027import javax.persistence.EntityManager;
028
029import org.apache.commons.logging.Log;
030import org.apache.commons.logging.LogFactory;
031import org.jboss.seam.ScopeType;
032import org.jboss.seam.annotations.Factory;
033import org.jboss.seam.annotations.In;
034import org.jboss.seam.annotations.Name;
035import org.jboss.seam.annotations.Scope;
036import org.nuxeo.common.utils.IdUtils;
037import org.nuxeo.ecm.core.api.CoreSession;
038import org.nuxeo.ecm.core.api.DocumentModel;
039import org.nuxeo.ecm.core.api.DocumentSecurityException;
040import org.nuxeo.ecm.core.api.NuxeoException;
041import org.nuxeo.ecm.core.api.PathRef;
042import org.nuxeo.ecm.core.api.RecoverableClientException;
043import org.nuxeo.ecm.core.persistence.PersistenceProviderFactory;
044import org.nuxeo.ecm.directory.DirectoryException;
045import org.nuxeo.ecm.directory.Session;
046import org.nuxeo.ecm.directory.api.DirectoryService;
047import org.nuxeo.ecm.platform.audit.api.AuditReader;
048import org.nuxeo.ecm.platform.audit.api.LogEntry;
049import org.nuxeo.ecm.platform.audit.api.Logs;
050import org.nuxeo.ecm.platform.audit.service.LogEntryProvider;
051import org.nuxeo.runtime.api.Framework;
052import org.nuxeo.runtime.transaction.TransactionHelper;
053
054/**
055 * Seam component performing errors
056 *
057 * @author Anahide Tchertchian
058 */
059@Name("errorSeamComponent")
060@Scope(ScopeType.CONVERSATION)
061public class SeamErrorComponent implements Serializable {
062
063    private static final long serialVersionUID = 1L;
064
065    private static final Log log = LogFactory.getLog(SeamErrorComponent.class);
066
067    @In(create = true, required = false)
068    protected transient CoreSession documentManager;
069
070    protected void createNewDocument() {
071        if (documentManager == null) {
072            log.error("********** Unexpected exception while testing "
073                    + "error handling: documentManager is null ***********");
074        }
075        DocumentModel newDocument = documentManager.createDocumentModel("Workspace");
076        String title = "Test document";
077        newDocument.setProperty("dublincore", "title", "Test document");
078        String parentDocumentPath = "/default-domain/workspaces";
079        String name = IdUtils.generateId(title, "-", true, 24);
080        newDocument.setPathInfo(parentDocumentPath, name);
081
082        newDocument = documentManager.createDocument(newDocument);
083        documentManager.save();
084    }
085
086    public void checkedErrorAfterCreation() {
087        createNewDocument();
088        throw new NuxeoException("Checked exception after document creation");
089    }
090
091    public void uncheckedErrorAfterCreation() {
092        try {
093            createNewDocument();
094        } catch (NuxeoException e) {
095            log.error("********** Unexpected exception while testing error handling ***********");
096        }
097        throw new NullPointerException("Unchecked exception after document creation");
098    }
099
100    public String getCheckedError() {
101        throw new NuxeoException("Checked error on getter");
102    }
103
104    public String getUncheckedError() {
105        throw new NullPointerException("Unchecked error on getter");
106    }
107
108    public String getSecurityError() throws DocumentSecurityException {
109        throw new DocumentSecurityException("Security error on getter");
110    }
111
112    @Factory(value = "checkedErrorFactoryEvent", scope = EVENT)
113    public String getCheckedErrorFactoryEvent() {
114        throw new NuxeoException("Checked error on factory, scope event");
115    }
116
117    @Factory(value = "uncheckedErrorFactoryEvent", scope = EVENT)
118    public String getUncheckedErrorFactoryEvent() {
119        throw new NullPointerException("Unchecked error on factory, scope event");
120    }
121
122    @Factory(value = "securityErrorFactoryEvent", scope = EVENT)
123    public String getSecurityErrorFactoryEvent() throws DocumentSecurityException {
124        throw new DocumentSecurityException("Security error on factory, scope event");
125    }
126
127    public String performCheckedError() {
128        throw new NuxeoException("Checked error on action");
129    }
130
131    public String performUncheckedError() {
132        throw new NullPointerException("Unchecked error on action");
133    }
134
135    public String performSecurityError() throws DocumentSecurityException {
136        throw new DocumentSecurityException("Security error on action");
137    }
138
139    /**
140     * @since 5.8
141     */
142    public String performRecoverableClientException() throws RecoverableClientException {
143        throw new RecoverableClientException("Application validation failed, rollingback",
144                "Application validation failed, rollingback", null);
145    }
146
147    /**
148     * @since 5.8
149     */
150    public String performPureRollback() {
151        TransactionHelper.setTransactionRollbackOnly();
152        return null;
153    }
154
155    /**
156     * @since 5.9.5
157     */
158    public void performDistributedRollback() {
159        createDummyUser();
160        createDummyLogEntry();
161        createDummyDoc();
162        TransactionHelper.setTransactionRollbackOnly();
163    }
164
165    /**
166     * @since 5.9.5
167     */
168    public void clearDistributedRollbackEnv() {
169        clearDummyUser();
170        clearDummyDoc();
171        clearDummyLogEntries();
172    }
173
174    protected DocumentModel createDummyUser() {
175        DirectoryService directories = Framework.getLocalService(DirectoryService.class);
176        try (Session userDir = directories.getDirectory("userDirectory").getSession()) {
177            Map<String, Object> user = new HashMap<>();
178            user.put("username", "dummy");
179            user.put("password", "dummy");
180            user.put("firstName", "dummy");
181            user.put("lastName", "dummy");
182            return userDir.createEntry(user);
183        }
184    }
185
186    protected void clearDummyUser() throws DirectoryException {
187        DirectoryService directories = Framework.getLocalService(DirectoryService.class);
188        try (Session userDir = directories.open("userDirectory")) {
189            userDir.deleteEntry("dummy");
190        }
191    }
192
193    /**
194     * @since 5.9.5
195     */
196    @Factory(scope = ScopeType.EVENT)
197    public boolean isDummyUserExists() throws DirectoryException {
198        DirectoryService directories = Framework.getLocalService(DirectoryService.class);
199        try (Session userDir = directories.getDirectory("userDirectory").getSession()) {
200            DocumentModel user = userDir.getEntry("dummy");
201            return user != null;
202        } catch (DirectoryException cause) {
203            return false;
204        }
205    }
206
207    protected LogEntry createDummyLogEntry() {
208        Logs logs = Framework.getLocalService(Logs.class);
209        LogEntry entry = logs.newLogEntry();
210        entry.setEventId("dummy");
211        entry.setDocUUID("dummy");
212        entry.setCategory("dummy");
213        entry.setComment("dummy");
214        logs.addLogEntries(Collections.singletonList(entry));
215        return entry;
216    }
217
218    /**
219     * @since 5.9.5
220     */
221    public void clearDummyLogEntries() {
222        PersistenceProviderFactory pf = Framework.getService(PersistenceProviderFactory.class);
223        EntityManager em = pf.newProvider("nxaudit-logs").acquireEntityManager();
224        LogEntryProvider provider = LogEntryProvider.createProvider(em);
225        provider.removeEntries("dummy", null);
226    }
227
228    /**
229     * @since 5.9.5
230     */
231    @Factory(scope = ScopeType.EVENT)
232    public boolean isDummyAuditExists() {
233        AuditReader reader = Framework.getLocalService(AuditReader.class);
234        List<LogEntry> entries = reader.getLogEntriesFor("dummy");
235        return !entries.isEmpty();
236    }
237
238    protected DocumentModel createDummyDoc() {
239        DocumentModel doc = documentManager.createDocumentModel("/", "dummy", "Document");
240        doc = documentManager.createDocument(doc);
241        documentManager.save();
242        return doc;
243    }
244
245    /**
246     * @since 5.9.5
247     */
248    public void clearDummyDoc() {
249        PathRef ref = new PathRef("/dummy");
250        if (documentManager.exists(ref)) {
251            documentManager.removeDocument(ref);
252        }
253    }
254
255    /**
256     * @since 5.9.5
257     */
258    @Factory(scope = ScopeType.EVENT)
259    public boolean isDummyDocExists() {
260        return documentManager.exists(new PathRef("/dummy"));
261    }
262
263    // methods to test concurrency issues
264
265    protected int counter = 0;
266
267    /**
268     * @since 5.8
269     */
270    public String performConcurrentRequestTimeoutException() throws Exception {
271        Thread.sleep(15 * 1000);
272        counter++;
273        return null;
274    }
275
276    public int getCounterValue() {
277        return counter;
278    }
279
280}