de.knowwe.jspwiki.JSPWikiConnector.java Source code

Java tutorial

Introduction

Here is the source code for de.knowwe.jspwiki.JSPWikiConnector.java

Source

/*
 * Copyright (C) 2009 Chair of Artificial Intelligence and Applied Informatics
 * Computer Science VI, University of Wuerzburg
 * 
 * This is free software; you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 3 of the License, or (at your option) any
 * later version.
 * 
 * This software is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this software; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
 * site: http://www.fsf.org.
 */

package de.knowwe.jspwiki;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.io.IOUtils;
import org.apache.wiki.InternalWikiException;
import org.apache.wiki.PageLock;
import org.apache.wiki.PageManager;
import org.apache.wiki.WikiContext;
import org.apache.wiki.WikiEngine;
import org.apache.wiki.WikiPage;
import org.apache.wiki.WikiProvider;
import org.apache.wiki.api.exceptions.ProviderException;
import org.apache.wiki.api.exceptions.WikiException;
import org.apache.wiki.attachment.Attachment;
import org.apache.wiki.attachment.AttachmentManager;
import org.apache.wiki.auth.AuthorizationManager;
import org.apache.wiki.auth.SessionMonitor;
import org.apache.wiki.auth.WikiSecurityException;
import org.apache.wiki.auth.permissions.PagePermission;
import org.apache.wiki.auth.permissions.PermissionFactory;
import org.apache.wiki.preferences.Preferences;
import org.apache.wiki.providers.CachingAttachmentProvider;
import org.apache.wiki.providers.KnowWEAttachmentProvider;
import org.apache.wiki.providers.WikiAttachmentProvider;
import org.apache.wiki.util.TextUtil;
import org.jetbrains.annotations.Nullable;

import com.denkbares.utils.Log;
import com.denkbares.utils.Pair;
import de.knowwe.core.Attributes;
import de.knowwe.core.Environment;
import de.knowwe.core.user.UserContext;
import de.knowwe.core.utils.KnowWEUtils;
import de.knowwe.core.wikiConnector.WikiAttachment;
import de.knowwe.core.wikiConnector.WikiAttachmentInfo;
import de.knowwe.core.wikiConnector.WikiConnector;
import de.knowwe.core.wikiConnector.WikiPageInfo;

/**
 * For code documentation look at the WikiConnector interface definition
 *
 * @author Jochen
 */
public class JSPWikiConnector implements WikiConnector {

    private ServletContext context = null;
    private WikiEngine engine = null;

    private static final Map<String, List<WikiAttachment>> zipAttachmentCache = Collections
            .synchronizedMap(new HashMap<>());

    private static int skipCount = 0;
    private static final int skipAfter = 10;

    public WikiEngine getEngine() {
        return engine;
    }

    public static final String LINK_PREFIX = "Wiki.jsp?page=";

    private static final Pattern ZIP_PATTERN = Pattern.compile("^([^/]+/[^/]+\\.zip)/(.+$)");

    public JSPWikiConnector(WikiEngine eng) {
        this.context = eng.getServletContext();
        this.engine = eng;
        initPageLocking();
    }

    /**
     * We need this method, because there is the possibility of an IllegalThreadStateException while initializing the
     * lock reaper of the PageManager, if the first call of lockPage is done in parallel by two threads. To avoid this,
     * we call this directly at startup in a safe way.
     *
     * @created 26.10.2012
     */
    private void initPageLocking() {
        PageManager mgr = engine.getPageManager();
        WikiPage page = new WikiPage(engine, "Dummy");
        PageLock lock = mgr.lockPage(page, "Dummy");
        mgr.unlockPage(lock);
    }

    @Override
    public @Nullable String getTemplate() {
        return getEngine().getWikiProperties().getProperty("jspwiki.templateDir");
    }

    @Override
    public String createArticle(String title, String author, String content) {
        return createArticle(title, author, content, true, true);
    }

    public String createArticle(String title, String author, String content, boolean updateReferences,
            boolean reindex) {

        WikiPage wp = new WikiPage(this.engine, title);

        try {
            if (updateReferences) {
                updateReferences(title);
            }
            wp.setAuthor(author);
            this.engine.getPageManager().putPageText(wp, content);
            if (reindex) {
                reindex(title);
            }

        } catch (ProviderException e) {
            Log.severe(e.getMessage(), e);
            return null;
        } catch (NullPointerException e) {
            // should only happen on wiki initialization
            return null;
        }
        Environment.getInstance().buildAndRegisterArticle(Environment.DEFAULT_WEB, title, content);
        return this.engine.getPureText(wp);
    }

    public void reindex(String title) {
        WikiPage wp = this.engine.getPage(title);
        this.engine.getSearchManager().reindexPage(wp);
    }

    @SuppressWarnings("unchecked")
    public void updateReferences(String title) {
        this.engine.getReferenceManager().updateReferences(title, this.engine.getReferenceManager().findCreated());
    }

    @Override
    public boolean doesArticleExist(String title) {

        // Check if a Page with the chosen title already exists.
        return this.engine.pageExists(title);
    }

    @Override
    public String[] getAllActiveUsers() {
        String[] activeUsers;

        Principal[] principals = SessionMonitor.getInstance(engine).userPrincipals();
        activeUsers = new String[principals.length];
        for (int i = 0; i < principals.length; i++) {
            activeUsers[i] = principals[i].getName();
        }

        return activeUsers;
    }

    @SuppressWarnings("unchecked")
    @Override
    public Map<String, String> getAllArticles(String web) {
        Map<String, String> result = new HashMap<>();
        Collection<WikiPage> pages = null;
        PageManager pageManager = this.engine.getPageManager();
        try {
            pages = pageManager.getProvider().getAllPages();
        } catch (ProviderException e) {
            Log.severe("Exception while retrieving articles.", e);
        }
        if (pages == null)
            return null;

        for (WikiPage wikiPage : pages) {
            String pageContent = null;
            try {
                pageContent = pageManager.getPageText(wikiPage.getName(), wikiPage.getVersion());
            } catch (ProviderException e) {
                Log.severe("Exception while retrieving articles.", e);
            }
            if (pageContent != null) {
                result.put(wikiPage.getName(), pageContent);
            }

        }

        return result;
    }

    @Override
    public String[] getAllUsers() {
        String[] users = null;

        try {
            Principal[] princ = engine.getUserManager().listWikiNames();
            users = new String[princ.length];
            for (int i = 0; i < princ.length; i++) {
                users[i] = engine.getUserManager().getUserDatabase().findByWikiName(princ[i].getName())
                        .getFullname();
            }
        } catch (WikiSecurityException e) {
            Log.severe("Exception while retrieving users.", e);
        }

        return users;
    }

    @Override
    public String getApplicationRootPath() {
        return getServletContext().getRealPath("").replaceAll("/+$", "");
    }

    @Override
    public Collection<WikiAttachment> getAttachments() throws IOException {
        cleanZipAttachmentCache();
        try {
            AttachmentManager attachmentManager = this.engine.getAttachmentManager();
            Collection<?> attachments = attachmentManager.getAllAttachments();
            Collection<WikiAttachment> wikiAttachments = new ArrayList<>(attachments.size());
            for (Object o : attachments) {
                if (o instanceof Attachment) {
                    Attachment att = (Attachment) o;
                    wikiAttachments.add(new JSPWikiAttachment(att, attachmentManager));
                    wikiAttachments.addAll(getZipEntryAttachments(att));
                }
            }
            return wikiAttachments;
        } catch (ProviderException e) {
            String message = "Cannot access attachments due to provider error.";
            throw new IOException(message, e);
        }

    }

    @Override
    public WikiAttachment getAttachment(String path) throws IOException {
        return getAttachment(path, -1);
    }

    private Pair<String, String> getActualPathAndEntry(String path) {
        Matcher matcher = ZIP_PATTERN.matcher(path);
        String entry = null;
        String actualPath = path;
        if (matcher.find()) {
            actualPath = matcher.group(1);
            entry = matcher.group(2);
        }
        return new Pair<>(actualPath, entry);
    }

    @Override
    public WikiAttachment getAttachment(String path, int version) throws IOException {
        try {

            Pair<String, String> actualPathAndEntry = getActualPathAndEntry(path);
            String actualPath = actualPathAndEntry.getA();
            String entry = actualPathAndEntry.getB();

            AttachmentManager attachmentManager = this.engine.getAttachmentManager();
            Attachment attachment = attachmentManager.getAttachmentInfo(actualPath, version);

            if (attachment == null) {
                return null;
            } else if (entry == null) {
                return new JSPWikiAttachment(attachment, attachmentManager);
            } else {
                InputStream attachmentStream = attachmentManager.getAttachmentStream(attachment);
                ZipInputStream zipStream = new ZipInputStream(attachmentStream);
                boolean found = false;
                for (ZipEntry e; (e = zipStream.getNextEntry()) != null;) {
                    if (e.getName().equals(entry)) {
                        found = true;
                        break;
                    }
                }
                if (!found)
                    throw new IOException("ZipEntry '" + entry + "' not found.");
                return new JSPWikiZipAttachment(entry, attachment, attachmentManager);
            }

        } catch (ProviderException e) {
            String message = "Cannot access attachments due to provider error.";
            throw new IOException(message, e);
        }
    }

    /**
     * Removes zip attachments of zip files that are no longer attached themselves.
     */
    private void cleanZipAttachmentCache() {
        // We call this method quite often, so we skip most of the
        // calls...
        if (skipCount++ < skipAfter) {
            return;
        } else {
            skipCount = 0;
        }
        List<String> remove = new ArrayList<>();
        for (String path : zipAttachmentCache.keySet()) {
            AttachmentManager attachmentManager = this.engine.getAttachmentManager();
            try {
                Attachment attachment = attachmentManager.getAttachmentInfo(path);
                if (attachment == null) {
                    remove.add(path);
                }
            } catch (ProviderException e) {
                Log.warning("Exception while cleaning zip cache", e);
            }
        }
        zipAttachmentCache.keySet().removeAll(remove);
    }

    private List<WikiAttachment> getZipEntryAttachments(Attachment attachment)
            throws IOException, ProviderException {
        if (!attachment.getFileName().endsWith(".zip"))
            return Collections.emptyList();
        List<WikiAttachment> zipEntryAttachments = zipAttachmentCache.get(attachment.getName());
        AttachmentManager attachmentManager = this.engine.getAttachmentManager();
        if (attachment.getVersion() == WikiProvider.LATEST_VERSION) {
            // little hack for JSPWiki 2.8.4 not always providing a correct
            // version number and we need it here.
            WikiAttachmentProvider currentProvider = attachmentManager.getCurrentProvider();
            // there only are two possible providers, the
            // BasicAttachmentProvider and the CachingAttachmentProvider
            // the BasicAttachmentProvider has a correct version number
            if (currentProvider instanceof CachingAttachmentProvider) {
                currentProvider = ((CachingAttachmentProvider) currentProvider).getRealProvider();
            }
            Attachment attachmentInfo = currentProvider.getAttachmentInfo(
                    new WikiPage(engine, attachment.getParentName()), attachment.getFileName(),
                    WikiProvider.LATEST_VERSION);
            // this attachmentInfo will have the correct version number, so
            // we set it for the actual attachment
            attachment.setVersion(attachmentInfo.getVersion());
        }
        if (zipEntryAttachments != null) {
            // we check if the attachments are outdated
            int cachedVersion = zipEntryAttachments.get(0).getVersion();
            int currentVersion = attachment.getVersion();
            if (cachedVersion != currentVersion) {
                zipEntryAttachments = null;
            }
        }
        if (zipEntryAttachments == null) {
            zipEntryAttachments = new ArrayList<>();

            InputStream attachmentStream = attachmentManager.getAttachmentStream(attachment);
            ZipInputStream zipStream = new ZipInputStream(attachmentStream);
            for (ZipEntry e; (e = zipStream.getNextEntry()) != null;) {
                zipEntryAttachments.add(new JSPWikiZipAttachment(e.getName(), attachment, attachmentManager));
            }
            zipStream.close();
            if (!zipEntryAttachments.isEmpty()) {
                zipAttachmentCache.put(attachment.getName(), zipEntryAttachments);
            }
        }
        return zipEntryAttachments;
    }

    @Override
    public List<WikiAttachment> getAttachments(String title) throws IOException {
        cleanZipAttachmentCache();
        try {
            // this list is in fact a Collection<Attachment>,
            // the conversion is type safe!
            AttachmentManager attachmentManager = this.engine.getAttachmentManager();
            WikiPage page = this.engine.getPage(title);
            if (page == null) {
                // might happen that a page of this title does not exist.
                // return empty list to prevent NullPointer in AttachmentManager
                return Collections.emptyList();
            }
            @SuppressWarnings("unchecked")
            Collection<Attachment> attList = attachmentManager.listAttachments(page);

            List<WikiAttachment> attachmentList = new ArrayList<>(attList.size());
            for (Attachment att : attList) {
                attachmentList.add(new JSPWikiAttachment(att, attachmentManager));
                attachmentList.addAll(getZipEntryAttachments(att));
            }

            return attachmentList;
        } catch (ProviderException e) {
            String message = "cannot access attachments due to provider error";
            throw new IOException(message, e);
        }
    }

    @Override
    public String getAuthor(String title, int version) {
        // Surrounded this because getPage()
        // caused a NullPointer on first KnowWE startup
        try {
            if ((this.engine == null) || (this.engine.getPage(title) == null))
                return null;
        } catch (NullPointerException e) {
            return null;
        }

        try {
            WikiContext context = new WikiContext(this.engine, this.engine.getPage(title));
            if (context.getEngine().pageExists(context.getPage().getName(), version)) {
                return context.getEngine().getPage(context.getPage().getName(), version).getAuthor();
            }
        } catch (ProviderException ignored) {
        }
        return null;
    }

    @Override
    public String getBaseUrl() {
        return engine.getWikiProperties().getProperty("jspwiki.baseURL", engine.getBaseURL());
    }

    @Override
    public String getKnowWEExtensionPath() {
        return KnowWEUtils.getRealPath(KnowWEUtils.getConfigBundle().getString("path_to_knowweextension"));
    }

    @Override
    public Date getLastModifiedDate(String title, int version) {
        // Surrounded this because getPage()
        // caused a NullPointer on first KnowWE startup
        try {
            if ((this.engine == null) || (this.engine.getPage(title) == null))
                return null;
        } catch (NullPointerException e) {
            return null;
        }

        WikiContext context = new WikiContext(this.engine, this.engine.getPage(title));
        try {
            if (context.getEngine().pageExists(context.getPage().getName(), version)) {
                return context.getEngine().getPage(context.getPage().getName(), version).getLastModified();
            }
        } catch (ProviderException ignored) {
        }
        return null;
    }

    @Override
    public Locale getLocale(HttpServletRequest request) {
        WikiContext wikiContext = new WikiContext(this.engine, request, this.engine.getPage("Main"));
        return Preferences.getLocale(wikiContext);
    }

    @Override
    public String getRealPath() {
        return context.getContextPath();
    }

    @Override
    public String getSavePath() {
        return (String) engine.getWikiProperties().get("var.basedir");
    }

    @Override
    public ServletContext getServletContext() {
        return context;
    }

    @Override
    public int getVersion(String title) {
        WikiContext context = new WikiContext(this.engine, this.engine.getPage(title));
        return context.getPage().getVersion();
    }

    @Override
    public String getArticleText(String title) {
        return getArticleText(title, -1);
    }

    @Override
    public String getArticleText(String title, int version) {
        // Surrounded this because getPage()
        // caused a NullPointer on first KnowWE startup
        try {
            if ((this.engine == null) || (this.engine.getPage(title) == null))
                return null;
        } catch (NullPointerException e) {
            return null;
        }

        String pageText;
        if (title.contains("/")) {
            // we have an attached article
            try {
                WikiAttachment attachment = getAttachment(title, version);
                if (attachment != null) {
                    InputStream inputStream = attachment.getInputStream();
                    return IOUtils.toString(inputStream);
                }
            } catch (IOException e) {
                Log.warning("Could not read attachment content from: " + title);
            }
        }
        try {
            pageText = engine.getPageManager().getPageText(title, version);
        } catch (ProviderException e) {
            Log.warning("Could not obtain page text from PageManager for: " + title);
            return null;
        }

        return pageText;
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<WikiPageInfo> getArticleHistory(String title) throws IOException {
        try {
            List<WikiPage> versionHistory = this.engine.getPageManager().getVersionHistory(title);
            if (versionHistory == null)
                return Collections.emptyList();
            if (versionHistory.isEmpty()) {
                // can happen in JSPWiki, if OLD was cleaned up manually
                WikiPage currentVersion = this.engine.getPage(title);
                versionHistory = Collections.singletonList(currentVersion);
            }
            return versionHistory.stream().map(page -> new WikiPageInfo(page.getName(), page.getAuthor(),
                    page.getVersion(), page.getLastModified())).collect(Collectors.toList());
        } catch (ProviderException e) {
            throw new IOException("Cannot access wiki page history of '" + title + "' due to provider error", e);
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<WikiAttachmentInfo> getAttachmentHistory(String name) throws IOException {
        try {
            List<Attachment> versionHistory = this.engine.getAttachmentManager().getVersionHistory(name);
            if (versionHistory == null)
                return Collections.emptyList();
            return versionHistory.stream().map(page -> new WikiAttachmentInfo(page.getName(), page.getAuthor(),
                    page.getVersion(), page.getLastModified())).collect(Collectors.toList());
        } catch (ProviderException e) {
            throw new IOException("Cannot access attachment history of '" + name + "' due to provider error", e);
        }
    }

    @Override
    public String getChangeNote(String title, int version) {
        PageManager pm = engine.getPageManager();
        try {
            WikiPage pageInfo = pm.getPageInfo(title, version);
            if (pageInfo == null) {
                return null;
            }
            String note = (String) pageInfo.getAttribute(WikiPage.CHANGENOTE);
            return note == null ? "" : note;
        } catch (ProviderException e) {
            Log.severe("Exception while retrieving change notes.", e);
        }

        return null;
    }

    public String getWikiProperty(String property) {
        return (String) engine.getWikiProperties().get(property);
    }

    /**
     * Checks if the current page has an access lock. If TRUE no user other then the lock owner can edit the page. If
     * FALSE the current page has no lock and can be edited by anyone.
     *
     * @param title the title of the article to check
     */
    @Override
    public boolean isArticleLocked(String title) {
        PageManager mgr = engine.getPageManager();
        WikiPage page = new WikiPage(engine, title);
        return mgr.getCurrentLock(page) != null;
    }

    /**
     * Checks if the current page has been locked by the current user. Returns TRUE if yes, FALSE otherwise.
     *
     * @param title the title of the article to check
     * @param user  the user to check for
     */
    @Override
    public boolean isArticleLockedCurrentUser(String title, String user) {
        PageManager mgr = engine.getPageManager();
        WikiPage page = new WikiPage(engine, title);
        PageLock lock = mgr.getCurrentLock(page);
        return lock != null && lock.getLocker().equals(user);

    }

    @Override
    public boolean lockArticle(String title, String user) {
        PageManager mgr = engine.getPageManager();
        WikiPage page = new WikiPage(engine, title);
        PageLock lock = mgr.lockPage(page, user);
        return lock != null;
    }

    @Override
    public String normalizeString(String string) {
        return TextUtil.normalizePostData(string);
    }

    @Override
    public String renderWikiSyntax(String content, HttpServletRequest request) {
        try {
            WikiContext context = engine.createContext(request, WikiContext.VIEW);
            content = engine.textToHTML(context, content);
        } catch (InternalWikiException e) {
            // happens only during KnowWE's startup and can thus be ignored...
        }
        return content;
    }

    @Override
    public String renderWikiSyntax(String string) {
        return renderWikiSyntax(string, null);
    }

    @Override
    public WikiAttachment storeAttachment(String title, String user, File attachmentFile) throws IOException {
        try (FileInputStream in = new FileInputStream(attachmentFile)) {
            return storeAttachment(title, attachmentFile.getName(), user, in);
        }
    }

    @Override
    public WikiAttachment storeAttachment(String title, String filename, String user, InputStream stream)
            throws IOException {
        try {
            boolean wasLocked = isArticleLocked(title);
            if (!wasLocked)
                lockArticle(title, user);
            AttachmentManager attachmentManager = this.engine.getAttachmentManager();

            Attachment attachment = new Attachment(engine, title, filename);
            attachment.setAuthor(user);
            attachmentManager.storeAttachment(attachment, stream);
            String path = toPath(title, filename);
            Log.info("Stored attachment '" + path + "'");
            if (!wasLocked)
                unlockArticle(title, user);
            return getAttachment(toPath(title, filename));
        } catch (ProviderException e) {
            throw new IOException("could not store attachment");
        }
    }

    @Override
    public WikiAttachment storeAttachment(String title, String filename, String user, InputStream stream,
            boolean versioning) throws IOException {
        if (!versioning) {
            // we just delete the current attachment version and don't fire delete-event
            // there will be a stored-event
            deleteAttachment(title, filename, user, false);
        }
        return storeAttachment(title, filename, user, stream);
    }

    @Override
    public void deleteAttachment(String title, String fileName, String user) throws IOException {
        deleteAttachment(title, fileName, user, true);
    }

    private void deleteAttachment(String title, String fileName, String user, boolean fireDeleteEvent)
            throws IOException {
        String path = toPath(title, fileName);
        Pair<String, String> actualPathAndEntry = getActualPathAndEntry(path);
        if (actualPathAndEntry.getB() != null) {
            throw new IOException("Unable to delete zip entry (" + path
                    + ") in zip attachment. Try to delete attachment instead.");
        }
        try {
            boolean wasLocked = isArticleLocked(title);
            if (!wasLocked)
                lockArticle(title, user);
            AttachmentManager attachmentManager = this.engine.getAttachmentManager();
            Attachment attachment = attachmentManager.getAttachmentInfo(path);

            if (attachment != null && !fireDeleteEvent) {
                // will cause the KnowWEAttachmentProvider to not fire a delete event
                // not pretty, but the JSPWiki API is not on our side here
                attachment.setAttribute(KnowWEAttachmentProvider.FIRE_DELETE_EVENT, "false");
            }

            if (attachment != null) {
                attachmentManager.deleteAttachment(attachment);
                Log.info("Deleted attachment '" + path + "'");
            }
            if (!wasLocked)
                unlockArticle(title, user);
        } catch (ProviderException e) {
            throw new IOException(e);
        }
    }

    public static String toPath(String articleTitle, String fileName) {
        return articleTitle + "/" + fileName;
    }

    @Override
    public void unlockArticle(String title, String user) {
        PageManager mgr = engine.getPageManager();
        WikiPage page = new WikiPage(engine, title);

        if (isArticleLocked(title)) {
            if (user == null) {
                PageLock lock = mgr.getCurrentLock(page);
                mgr.unlockPage(lock);
            } else {
                for (Object other : mgr.getActiveLocks()) {
                    PageLock otherLock = (PageLock) other;
                    if (otherLock.getLocker().equals(user)) {
                        mgr.unlockPage(otherLock);
                    }
                }
            }
        }
    }

    @Override
    public boolean userCanEditArticle(String title, HttpServletRequest request) {
        if (ReadOnlyManager.isReadOnly())
            return false;
        WikiPage page = new WikiPage(engine, title);
        WikiContext context = new WikiContext(this.engine, request, this.engine.getPage(title));

        AuthorizationManager authmgr = engine.getAuthorizationManager();
        //noinspection SynchronizationOnLocalVariableOrMethodParameter
        synchronized (authmgr) {
            PagePermission pp = PermissionFactory.getPagePermission(page, "edit");
            try {
                return authmgr.checkPermission(context.getWikiSession(), pp);
            } catch (StackOverflowError e) {
                // happens with very large articles
                Log.severe("StackOverflowError while checking permissions on article '" + title + "': "
                        + e.getMessage());
                return false;
            }
        }
    }

    @Override
    public boolean userCanViewArticle(String title, HttpServletRequest request) {
        WikiPage page = new WikiPage(engine, title);
        WikiContext context = new WikiContext(this.engine, request, this.engine.getPage(title));

        AuthorizationManager authmgr = engine.getAuthorizationManager();
        //noinspection SynchronizationOnLocalVariableOrMethodParameter
        synchronized (authmgr) {
            PagePermission pp = PermissionFactory.getPagePermission(page, "view");
            return authmgr.checkPermission(context.getWikiSession(), pp);
        }
    }

    @Override
    public boolean userIsMemberOfGroup(String groupname, HttpServletRequest request) {

        // which article is not relevant
        String articleName = "Main";
        WikiContext context = new WikiContext(this.engine, request, this.engine.getPage(articleName));

        Principal[] princ = context.getWikiSession().getRoles();

        for (Principal p : princ) {
            if (p.getName().equals(groupname))
                return true;
        }

        return false;
    }

    @Override
    public boolean writeArticleToWikiPersistence(String title, String content, UserContext user) {
        try {
            WikiPage page = engine.getPage(title);
            WikiContext context = engine.createContext(user.getRequest(), WikiContext.EDIT);
            page.setAuthor(context.getCurrentUser().getName());
            String changeNote = user.getParameter(Attributes.CHANGE_NOTE);
            if (changeNote != null) {
                page.setAttribute(WikiPage.CHANGENOTE, changeNote);
            }
            context.setPage(page);
            context.setRealPage(page);

            engine.saveText(context, content);
            return true;
        } catch (WikiException e) {
            Log.severe("Failed to write article changes to wiki persistence", e);
            return false;
        }
    }
}