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