nl.edia.sakai.tool.skinmanager.impl.SkinServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for nl.edia.sakai.tool.skinmanager.impl.SkinServiceImpl.java

Source

/*
 * #%L
 * Edia Skin Manager Logic Impl
 * %%
 * Copyright (C) 2007 - 2013 Edia
 * %%
 * Licensed under the Educational Community License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *             http://opensource.org/licenses/ECL-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
package nl.edia.sakai.tool.skinmanager.impl;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import nl.edia.sakai.tool.skinmanager.ActionNotAlowedException;
import nl.edia.sakai.tool.skinmanager.Permissions;
import nl.edia.sakai.tool.skinmanager.SkinArchiveService;
import nl.edia.sakai.tool.skinmanager.SkinException;
import nl.edia.sakai.tool.skinmanager.SkinFileSystemService;
import nl.edia.sakai.tool.skinmanager.SkinInUseException;
import nl.edia.sakai.tool.skinmanager.SkinPrerequisitesNonFatalException;
import nl.edia.sakai.tool.skinmanager.SkinService;
import nl.edia.sakai.tool.skinmanager.model.SkinArchive;
import nl.edia.sakai.tool.skinmanager.model.SkinDirectory;
import nl.edia.sakai.tool.util.SakaiUtils;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sakaiproject.authz.api.FunctionManager;
import org.sakaiproject.component.cover.ServerConfigurationService;
import org.sakaiproject.exception.PermissionException;
import org.sakaiproject.portal.api.PortalService;
import org.sakaiproject.site.api.Site;

public class SkinServiceImpl implements SkinService {

    private static final boolean BOOLEAN_ACTIVE_SYNC = false;
    /**
     * The amount of time each redeploy inteval runs, only 
     */
    private static final int INTERVAL_REDEPLOY = 30 * 1000;
    private static final int INTERVAL_WAIT = 10 * 1000;

    protected SkinFileSystemService skinFileSystemService;

    protected SkinArchiveService skinArchiveService;

    protected PortalService portalService;

    protected FunctionManager functionManager;

    boolean isArchiveLeading = true;

    static Log log = LogFactory.getLog(SkinServiceImpl.class);

    private Thread theSyncThread;

    /* (non-Javadoc)
     * @see nl.edia.sakai.tool.skinmanager.SkinService#createSkin(java.lang.String, java.io.InputStream)
     */
    @Override
    public void createSkin(String name, InputStream data, Date date) throws SkinException, IOException {
        checkAction(Permissions.PERMISSION_EDIA_SAKAI_SKININSTALL_CREATE);

        byte[] myFileData = readStream(data);
        if (date == null) {
            date = skinArchiveService.fetchSkinArchiveDate(name);
        }

        skinFileSystemService.createSkin(name, new ByteArrayInputStream(myFileData), date, false);
        createArchive(name, myFileData, date);

        SakaiUtils.createModificationEvent(Permissions.PERMISSION_EDIA_SAKAI_SKININSTALL_CREATE, "skin:" + name);
    }

    /* (non-Javadoc)
     * @see nl.edia.sakai.tool.skinmanager.SkinService#fetchInstalledSkins()
     */
    @Override
    public List<SkinDirectory> fetchInstalledSkins() throws SkinException, IOException {
        checkAction(Permissions.PERMISSION_EDIA_SAKAI_SKININSTALL_VIEW);
        return skinFileSystemService.fetchInstalledSkins();
    }

    /* (non-Javadoc)
     * @see nl.edia.sakai.tool.skinmanager.SkinService#fetchInstalledSkinNames()
     */
    @Override
    public List<String> fetchInstalledSkinNames() throws SkinException, IOException {
        // Maybe a bit fanatic, but at least check if the user is supposed to be here
        checkAction(org.sakaiproject.site.api.SiteService.SITE_VISIT);
        List<SkinDirectory> myFetchInstalledSkins = skinFileSystemService.fetchInstalledSkins();
        List<String> myList = new ArrayList<String>(myFetchInstalledSkins.size());
        for (SkinDirectory dir : myFetchInstalledSkins) {
            myList.add(dir.getName());
        }
        return myList;
    }

    /* (non-Javadoc)
     * @see nl.edia.sakai.tool.skinmanager.SkinService#fetchAvailableSkinNames()
     */
    @Override
    public List<String> fetchAvailableSkinNames() throws SkinException, IOException {
        // Maybe a bit fanatic, but at least check if the user is supposed to be here
        checkAction(org.sakaiproject.site.api.SiteService.SITE_VISIT);
        List<SkinDirectory> installedSkins = skinFileSystemService.fetchInstalledSkins();
        List<String> rv = new ArrayList<String>(installedSkins.size());
        String skinPrefix = portalService.getSkinPrefix();
        for (SkinDirectory dir : installedSkins) {
            String name = dir.getName();
            if (StringUtils.isEmpty(skinPrefix) || StringUtils.startsWith(name, skinPrefix)) {
                rv.add(name);
            }
        }
        return rv;
    }

    /* (non-Javadoc)
     * @see nl.edia.sakai.tool.skinmanager.SkinService#fetchSkinArchive(java.lang.String)
     */
    @Override
    public SkinArchive fetchSkinArchive(String name) throws SkinException, IOException {
        return skinArchiveService.findSkinArchive(name);
    }

    /* (non-Javadoc)
     * @see nl.edia.sakai.tool.skinmanager.SkinService#findSites(java.lang.String)
     */
    @Override
    public List<Site> findSites(String name) {
        return skinArchiveService.findSites(name, getDefaultSkinName().equals(name));
    }

    /* (non-Javadoc)
     * @see nl.edia.sakai.tool.skinmanager.SkinService#findSkin(java.lang.String)
     */
    @Override
    public SkinDirectory findSkin(String id) throws SkinException, IOException {
        checkAction(Permissions.PERMISSION_EDIA_SAKAI_SKININSTALL_VIEW);
        SakaiUtils.createEvent(Permissions.PERMISSION_EDIA_SAKAI_SKININSTALL_VIEW, "skin:" + id);
        return skinFileSystemService.findSkin(id);
    }

    /* (non-Javadoc)
     * @see nl.edia.sakai.tool.skinmanager.SkinService#findSkinHistory(java.lang.String)
     */
    @Override
    public List<SkinArchive> findSkinHistory(String name) throws ActionNotAlowedException {
        checkAction(Permissions.PERMISSION_EDIA_SAKAI_SKININSTALL_VIEW);
        return skinArchiveService.findSkinHistory(name);
    }

    /**
     * @return
     */
    public SkinArchiveService getSkinArchiveService() {
        return skinArchiveService;
    }

    /**
     * @return
     */
    public SkinFileSystemService getSkinFileSystemService() {
        return skinFileSystemService;
    }

    /**
     * <p>
     * Does an initialization of the service.
     * </p>
     * @throws Exception
     */
    public void init() throws Exception {
        functionManager.registerFunction(Permissions.PERMISSION_EDIA_SAKAI_SKININSTALL_VIEW);
        functionManager.registerFunction(Permissions.PERMISSION_EDIA_SAKAI_SKININSTALL_CREATE);
        functionManager.registerFunction(Permissions.PERMISSION_EDIA_SAKAI_SKININSTALL_EDIT);
        functionManager.registerFunction(Permissions.PERMISSION_EDIA_SAKAI_SKININSTALL_DELETE);

        final int waitIntervalRetry = ServerConfigurationService.getInt("skinmanager.sync.interval",
                INTERVAL_REDEPLOY);
        final boolean activeSync = ServerConfigurationService.getBoolean("skinmanager.sync.continious",
                BOOLEAN_ACTIVE_SYNC);

        Runnable myTask = new Runnable() {
            @Override
            public void run() {
                boolean initialDeployDone = false;
                while (activeSync || !initialDeployDone) {
                    try {
                        syncDatabaseAndFileSystem();
                        initialDeployDone = true;
                    } catch (SkinPrerequisitesNonFatalException e) {
                        log.info("Non-fatal problem attempting to init the skin manager service, retrying in "
                                + (INTERVAL_REDEPLOY / 1000) + " seconds", e);
                    } catch (SkinException e) {
                        log.error("Fatal problem attempting to init the skin manager service", e);
                        initialDeployDone = true;
                    } catch (IOException e) {
                        log.error("Fatal problem attempting to init the skin manager service", e);
                        initialDeployDone = true;
                    }
                    synchronized (SkinServiceImpl.this) {
                        try {
                            SkinServiceImpl.this.wait(waitIntervalRetry);
                        } catch (InterruptedException e1) {
                            // Just die.
                            return;
                        }
                    }
                }
            }
        };

        theSyncThread = new Thread(myTask, "Skin sync thread");
        // Die if you are the only one...
        theSyncThread.setDaemon(true);
        // Start the thread
        theSyncThread.start();

    }

    /**
     * 
     */
    public void destroy() {
        if (theSyncThread != null && theSyncThread.isAlive()) {
            theSyncThread.interrupt();
        }
    }

    protected void syncDatabaseAndFileSystem() throws SkinException, IOException {
        List<String> myInstalledSkinNames = new ArrayList<String>();
        // Add new skins to the archive
        for (SkinDirectory skinDirectory : skinFileSystemService.fetchInstalledSkins()) {
            String name = skinDirectory.getName();
            myInstalledSkinNames.add(name);
            SkinArchive skinArchive = skinArchiveService.findSkinArchive(name);
            if (skinArchive == null) {
                readSkin(name, skinDirectory);
            } else if (shouldOverwriteDeployedSkin(skinDirectory, skinArchive)) {
                final int interval = ServerConfigurationService.getInt("skinmanager.deploy.wait", INTERVAL_WAIT);
                synchronized (SkinServiceImpl.this) {
                    try {
                        SkinServiceImpl.this.wait(interval);
                    } catch (InterruptedException e) {
                        return;
                    }
                }
                installSkin(name, true);
            }
        }
        // Disable/restore skins that disappeared
        for (SkinArchive myArchive : skinArchiveService.fetchActiveSkinArchives()) {
            String name = myArchive.getName();
            if (!myInstalledSkinNames.contains(name)) {
                if (isArchiveLeading) {
                    installSkin(name, false);
                } else {
                    log.info("Skin archive removed form DB / not on filesystem: " + name);
                    skinArchiveService.removeSkinArchive(name);
                }
            }
        }
    }

    protected boolean shouldOverwriteDeployedSkin(SkinDirectory skinDirectory, SkinArchive skinArchive) {
        boolean overwrite = ServerConfigurationService.getBoolean("skinmanager.deploy.overwrite", false);
        return overwrite && !equals(skinArchive.getLastModified(), skinDirectory.getLastModified());
    }

    private boolean equals(Date a, Date b) {
        if (a == null || b == null)
            return false;
        long timeA = a.getTime();
        long timeB = b.getTime();
        return Math.abs(timeA - timeB) < 1000;
    }

    private void readSkin(String myName, SkinDirectory mySkinDirectory) throws IOException, SkinException {
        ByteArrayOutputStream myOutputStream = new ByteArrayOutputStream();
        try {
            skinFileSystemService.writeSkinData(myName, myOutputStream);
            ByteArrayInputStream myInputStream = new ByteArrayInputStream(myOutputStream.toByteArray());
            skinArchiveService.createSkinArchive(myName, myInputStream, mySkinDirectory.getLastModified(),
                    "Synchronized with filesystem");
            log.info("Skin archive created from filesystem: " + mySkinDirectory.getName());
        } finally {
            myOutputStream.close();
        }
    }

    /**
     * @deprecated Use {@link #installSkin(String,boolean)} instead
     */
    @Deprecated
    private void installSkin(String name) {
        installSkin(name, false);
    }

    private void installSkin(String name, boolean overWrite) {
        try {
            ByteArrayOutputStream myOutputStream = new ByteArrayOutputStream();
            final Date date = skinArchiveService.fetchSkinArchiveDate(name);
            skinArchiveService.fetchSkinArchiveData(name, myOutputStream);
            skinFileSystemService.createSkin(name, new ByteArrayInputStream(myOutputStream.toByteArray()), date,
                    overWrite);
            log.warn("Skin archive missing on filesystem, installed on FS: " + name);
        } catch (IOException e) {
            log.warn(e);
        } catch (SkinException e) {
            log.warn(e);
        }
    }

    /* (non-Javadoc)
     * @see nl.edia.sakai.tool.skinmanager.SkinService#isInUse(java.lang.String)
     */
    @Override
    public boolean isInUse(String name) throws ActionNotAlowedException {
        checkAction(Permissions.PERMISSION_EDIA_SAKAI_SKININSTALL_VIEW);
        return !findSites(name).isEmpty();
    }

    /* (non-Javadoc)
     * @see nl.edia.sakai.tool.skinmanager.SkinService#removeSkin(java.lang.String)
     */
    @Override
    public void removeSkin(String name) throws SkinException, IOException {
        checkAction(Permissions.PERMISSION_EDIA_SAKAI_SKININSTALL_DELETE);
        if (!isInUse(name)) {
            skinFileSystemService.removeSkin(name);
            skinArchiveService.removeSkinArchive(name);
            SakaiUtils.createModificationEvent(Permissions.PERMISSION_EDIA_SAKAI_SKININSTALL_DELETE,
                    "skin:" + name);
        } else {
            throw new SkinInUseException("SkinDirectory '" + name + "' is in use and cannot be deleted");
        }

    }

    /* (non-Javadoc)
     * @see nl.edia.sakai.tool.skinmanager.SkinService#restoreSkinHistory(java.lang.String, int)
     */
    @Override
    public void restoreSkinHistory(String name, int version) throws SkinException, IOException {
        checkAction(Permissions.PERMISSION_EDIA_SAKAI_SKININSTALL_EDIT);
        SkinArchive myLatestSkinArchive = skinArchiveService.findSkinArchive(name);
        if (myLatestSkinArchive != null) {
            int myLatestVersion = myLatestSkinArchive.getVersion();
            if (version < myLatestVersion) {
                ByteArrayOutputStream myOutputStream = new ByteArrayOutputStream();
                skinArchiveService.fetchSkinArchiveData(name, version, myOutputStream);
                updateSkin(name, myOutputStream.toByteArray(), "Reverted to version " + version, null);
            }
        }
    }

    /**
     * @param skinArchiveService
     */
    public void setSkinArchiveService(SkinArchiveService skinArchiveService) {
        this.skinArchiveService = skinArchiveService;
    }

    /**
     * @param skinFileSystemService
     */
    public void setSkinFileSystemService(SkinFileSystemService skinFileSystemService) {
        this.skinFileSystemService = skinFileSystemService;
    }

    /* (non-Javadoc)
     * @see nl.edia.sakai.tool.skinmanager.SkinService#updateSkin(java.lang.String, java.io.InputStream)
     */
    @Override
    public void updateSkin(String name, InputStream data, Date date) throws SkinException, IOException {
        updateSkin(name, readStream(data), null, date);
    }

    protected void checkAction(String myAction) throws ActionNotAlowedException {
        try {
            SakaiUtils.checkPermission(myAction);
        } catch (PermissionException p) {
            throw new ActionNotAlowedException("You are not allowed to execute this action");
        }
    }

    protected boolean equalsName(String name, Site mySite) {
        String mySkin = mySite.getSkin();

        String myDefaultSkinName = getDefaultSkinName();
        if (myDefaultSkinName.equals(name)) {
            return mySkin == null || myDefaultSkinName.equals(mySkin) || "".equals(mySkin);
        }
        return name.equals(mySkin);
    }

    protected String getDefaultSkinName() {
        String myDefaultSkinName = ServerConfigurationService.getString("skin.default");
        if (myDefaultSkinName == null) {
            myDefaultSkinName = "default";
        }
        return myDefaultSkinName;
    }

    protected byte[] readStream(InputStream data) throws IOException {
        ByteArrayOutputStream myOutputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int read;
        while ((read = data.read(buffer)) != -1) {
            myOutputStream.write(buffer, 0, read);
        }
        data.close();
        return myOutputStream.toByteArray();

    }

    private void createArchive(String name, byte[] myFileData, Date date) throws SkinException, IOException {
        SkinDirectory myNewSkin = skinFileSystemService.findSkin(name);
        if (date == null) {
            date = myNewSkin.getLastModified();
        }
        skinArchiveService.createSkinArchive(name, new ByteArrayInputStream(myFileData), date, "");
    }

    private void updateSkin(String name, byte[] myFileData, String comment, Date date)
            throws ActionNotAlowedException, SkinException, IOException {
        checkAction(Permissions.PERMISSION_EDIA_SAKAI_SKININSTALL_EDIT);
        if (date == null) {
            date = skinArchiveService.fetchSkinArchiveDate(name);
        }
        skinFileSystemService.updateSkin(name, date, new ByteArrayInputStream(myFileData));
        createArchive(name, myFileData, date);
        SakaiUtils.createModificationEvent(Permissions.PERMISSION_EDIA_SAKAI_SKININSTALL_VIEW, "skin:" + name);
    }

    public void setPortalService(PortalService portalService) {
        this.portalService = portalService;
    }

    public void setFunctionManager(FunctionManager functionManager) {
        this.functionManager = functionManager;
    }

}