org.efaps.ui.wicket.EFapsSession.java Source code

Java tutorial

Introduction

Here is the source code for org.efaps.ui.wicket.EFapsSession.java

Source

/*
 * Copyright 2003 - 2014 The eFaps Team
 *
 * Licensed under the Apache 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://www.apache.org/licenses/LICENSE-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.
 *
 * Revision:        $Rev$
 * Last Changed:    $Date$
 * Last Changed By: $Author$
 */

package org.efaps.ui.wicket;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Stack;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.wicket.Component;
import org.apache.wicket.RestartResponseException;
import org.apache.wicket.Session;
import org.apache.wicket.protocol.http.WebSession;
import org.apache.wicket.protocol.http.request.WebClientInfo;
import org.apache.wicket.protocol.http.servlet.ServletWebRequest;
import org.apache.wicket.request.IRequestParameters;
import org.apache.wicket.request.Request;
import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.util.string.StringValue;
import org.efaps.admin.access.AccessCache;
import org.efaps.admin.user.Person;
import org.efaps.admin.user.UserAttributesSet;
import org.efaps.api.background.IExecutionBridge;
import org.efaps.db.Context;
import org.efaps.jaas.LoginHandler;
import org.efaps.ui.wicket.components.IRecent;
import org.efaps.ui.wicket.components.menu.LinkItem;
import org.efaps.ui.wicket.connectionregistry.RegistryManager;
import org.efaps.ui.wicket.models.EmbeddedLink;
import org.efaps.ui.wicket.models.objects.UIMenuItem;
import org.efaps.ui.wicket.pages.error.ErrorPage;
import org.efaps.ui.wicket.request.EFapsMultipartRequest;
import org.efaps.ui.wicket.request.EFapsRequest;
import org.efaps.ui.wicket.util.Configuration;
import org.efaps.ui.wicket.util.Configuration.ConfigAttribute;
import org.efaps.util.EFapsException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A WebSession subclass that is used e.g. as a store for behaviors that last a
 * Session and provides functionalities like open/close a Context and
 * login/logout a User.
 *
 * @author The eFaps Team
 * @version $Id$
 */
public class EFapsSession extends WebSession {
    /**
     * This variable is used as the Key to the UserName stored in the
     * SessionAttributes.
     */
    public static final String LOGIN_ATTRIBUTE_NAME = "org.efaps.ui.wicket.LoginAttributeName";

    /**
     * Needed for serialization.
     */
    private static final long serialVersionUID = 1L;

    /**
     * Logger for this class.
     */
    private static final Logger LOG = LoggerFactory.getLogger(EFapsSession.class);

    /** The execution bridges. */
    private List<IExecutionBridge> executionBridges = Collections
            .synchronizedList(new ArrayList<IExecutionBridge>());

    /**
     * This instance Map is a Cache for Components, which must be able to be
     * accessed from various PageMaps.
     *
     * @see #getFromCache(String)
     * @see #putIntoCache(String, Component)
     * @see #removeFromCache(String)
     */
    private final Map<String, Component> componentcache = new HashMap<>();

    /**
     * This instance variable holds the Name of the logged in user. It is also
     * used to check if a user is logged in, by returning that a user is logged
     * in, if this variable is not null.
     *
     * @see #isLogedIn()
     * @see #checkin()
     * @see #checkout()
     */
    private String userName;

    /**
     * This instance map stores the Attributes which are valid for the whole
     * session. It is passed on to the Context while opening it.
     *
     * @see #openContext()
     */
    private final Map<String, Object> sessionAttributes = new HashMap<>();

    /**
     * File to be shown by the ShowFileCallBackBehavior.
     */
    private File file;

    /**
     * Stack that contains the recent visited components.
     */
    private final Stack<IRecent> recentStack = new Stack<>();

    /**
     * Links that are embeded in html, generated outside this wicket app.
     */
    private final List<EmbeddedLink> embededlinks = new ArrayList<>();

    /**
     * Size of the Stack for the recent objects.
     */
    private final int stackSize;

    /**
     * Standard Constructor from Wicket.
     *
     * @param _request Request
     * @param _appKey application key
     * @throws EFapsException
     */
    public EFapsSession(final Request _request, final String _appKey) {
        super(_request);
        this.stackSize = Configuration.getAttributeAsInteger(ConfigAttribute.RECENTCACHESIZE);
    }

    /**
     * Adds the execution bridge.
     *
     * @param _bridge the _bridge
     */
    public synchronized void addExecutionBridge(final IExecutionBridge _bridge) {
        bind();
        this.executionBridges.add(_bridge);
    }

    /**
     * Prune finished tasks.
     */
    public synchronized void pruneFinishedTasks() {
        final ArrayList<IExecutionBridge> nonFinishedBridges = new ArrayList<>();
        for (final IExecutionBridge bridge : this.executionBridges) {
            if (!bridge.isFinished()) {
                nonFinishedBridges.add(bridge);
            }
        }
        this.executionBridges = nonFinishedBridges;
    }

    /**
     * Gets the tasks page.
     *
     * @param _start the _start
     * @param _size the _size
     * @return the tasks page
     */
    public Iterator<IExecutionBridge> getJobsPage(final int _start, final int _size) {
        final int min = Math.min(_size, this.executionBridges.size());
        return new ArrayList<>(this.executionBridges.subList(_start, min)).iterator();
    }

    /**
     * Gets the bridge for job.
     *
     * @param _jobName the _job name
     * @param _prune the _prune
     * @return the bridge4 job
     */
    public IExecutionBridge getBridge4Job(final String _jobName, final boolean _prune) {
        IExecutionBridge ret = null;
        for (final IExecutionBridge bridge : this.executionBridges) {
            if (bridge.getJobName().equals(_jobName)) {
                ret = bridge;
                if (_prune && ret.isFinished()) {
                    this.executionBridges.remove(ret);
                }
                break;
            }
        }
        return ret;
    }

    /**
     * Count jobs.
     *
     * @return the long
     */
    public long countJobs() {
        return this.executionBridges.size();
    }

    /**
     * @param _recent Recent Object to be pushed on the stack.
     */
    public void addRecent(final IRecent _recent) {
        if (this.stackSize > 0) {
            this.recentStack.push(_recent);
            if (this.recentStack.size() > this.stackSize) {
                this.recentStack.remove(0);
            }
        }
        if (_recent instanceof LinkItem) {
            final Object object = ((LinkItem) _recent).getDefaultModelObject();
            if (object instanceof UIMenuItem) {
                UsageRegistry.register(((UIMenuItem) object).getKey4UsageRegistry());
            }
        }
    }

    @Override
    public WebClientInfo getClientInfo() {
        if (this.clientInfo == null) {
            final RequestCycle requestCycle = RequestCycle.get();
            this.clientInfo = new WebClientInfo(requestCycle);
        }
        return (WebClientInfo) this.clientInfo;
    }

    /**
     * @return the most recent object from the stack, null if stack is empty
     */
    public IRecent getRecent() {
        return this.recentStack.empty() ? null : this.recentStack.peek();
    }

    /**
     * @return a list with IRecent in the order that a sequential
     * pop on the stack would return
     */
    public List<IRecent> getAllRecents() {
        @SuppressWarnings("unchecked")
        final Stack<IRecent> clone = (Stack<IRecent>) this.recentStack.clone();
        Collections.reverse(clone);
        return clone;
    }

    /**
     * This Method stores a Component in the Cache.
     *
     * @param _key Key the Component should be stored in
     * @param _component Component to be stored
     * @see #componentcache
     */
    public void putIntoCache(final String _key, final Component _component) {
        this.componentcache.remove(_key);
        this.componentcache.put(_key, _component);
    }

    /**
     * Retrieve a Component from the ComponentCache.
     *
     * @param _key Key of the Component to be retrieved
     * @return Component if found, else null
     * @see #componentcache
     */
    public Component getFromCache(final String _key) {
        return this.componentcache.get(_key);
    }

    /**
     * Remove a Component from the ComponentCache.
     *
     * @param _key Key to the Component to be removed
     * @see #componentcache
     */
    public void removeFromCache(final String _key) {
        this.componentcache.remove(_key);
    }

    /**
     * Method to check if a user is checked in.
     *
     * @return true if a user is checked in, else false
     * @see #userName
     */
    public boolean isLogedIn() {
        boolean ret = false;
        if (this.userName != null) {
            ret = true;
        }
        return ret;

    }

    /**
     * Method to log a user with the Parameters from the Request in.
     *
     * @see #checkLogin(String, String)
     */
    public final void login() {
        final IRequestParameters paras = RequestCycle.get().getRequest().getRequestParameters();
        final StringValue name = paras.getParameterValue("name");
        final StringValue pwd = paras.getParameterValue("password");
        if (checkLogin(name.toString(), pwd.toString())) {
            this.userName = name.toString();
            // on login a valid Context for the User must be opened to ensure that the
            // session attributes that depend on the user are set correctly before any
            // further requests are made (e.g. setting the current company
            openContext();
            setAttribute(EFapsSession.LOGIN_ATTRIBUTE_NAME, this.userName);
            RegistryManager.registerUserSession(this.userName, getId());
        } else {
            this.userName = null;
            this.sessionAttributes.clear();
        }
    }

    /**
     * Logs a user out and stores the UserAttribues in the eFaps database.
     */
    public final void logout() {

        if (this.sessionAttributes.containsKey(UserAttributesSet.CONTEXTMAPKEY)) {
            try {
                UsageRegistry.store();
                ((UserAttributesSet) this.sessionAttributes.get(UserAttributesSet.CONTEXTMAPKEY)).storeInDb();
                AccessCache.clean4Person(Context.getThreadContext().getPersonId());
            } catch (final EFapsException e) {
                EFapsSession.LOG.error("Error on logout", e);
            } finally {
                this.sessionAttributes.clear();
                removeAttribute(EFapsSession.LOGIN_ATTRIBUTE_NAME);
                invalidate();
            }
        }
        closeContext();
        this.userName = null;
    }

    /**
     * method to check the LoginInformation (Name and Password) against the
     * eFapsDatabase. To check the Information a Context is opened an afterwards
     * closed. It also puts a new Instance of UserAttributes into the instance
     * map {@link #sessionAttributes}. The person returned must have at least one
     * role asigned to be confirmed as value.
     *
     * @param _name Name of the User to be checked in
     * @param _passwd Password of the User to be checked in
     * @return true if LoginInformation was valid, else false
     */
    private boolean checkLogin(final String _name, final String _passwd) {

        boolean loginOk = false;
        try {
            Context context = null;

            if (Context.isTMActive()) {
                context = Context.getThreadContext();
            } else {
                context = Context.begin();
            }
            boolean ok = false;

            try {
                // on a new login the cache for Person is reseted
                Person.initialize();
                final EFapsApplication app = (EFapsApplication) getApplication();
                final LoginHandler loginHandler = new LoginHandler(app.getApplicationKey());
                final Person person = loginHandler.checkLogin(_name, _passwd);
                if (person != null && !person.getRoles().isEmpty()) {
                    loginOk = true;
                    this.sessionAttributes.put(UserAttributesSet.CONTEXTMAPKEY, new UserAttributesSet(_name));
                }
                ok = true;
            } finally {
                if (ok && context.allConnectionClosed() && Context.isTMActive()) {
                    Context.commit();
                } else {
                    if (Context.isTMMarkedRollback()) {
                        EFapsSession.LOG.error("transaction is marked to roll back");
                    } else if (!context.allConnectionClosed()) {
                        EFapsSession.LOG.error("not all connection to database are closed");
                    } else {
                        EFapsSession.LOG.error("transaction manager in undefined status");
                    }
                    Context.rollback();
                }
            }
        } catch (final EFapsException e) {
            EFapsSession.LOG.error("could not check name and password", e);
        }
        return loginOk;
    }

    /**
     * Method that opens a new Context in eFaps, setting the User, the Locale,
     * the Attributes of this Session {@link #sessionAttributes} and the
     * RequestParameters for the Context.
     *
     * @see #attach()
     */
    public void openContext() {
        if (isLogedIn()) {
            try {
                if (!Context.isTMActive()) {
                    final ServletWebRequest request = (ServletWebRequest) RequestCycle.get().getRequest();
                    if (request instanceof EFapsRequest || request instanceof EFapsMultipartRequest) {
                        final Map<String, String[]> parameters = new HashMap<>();
                        final IRequestParameters reqPara = request.getRequestParameters();
                        for (final String name : reqPara.getParameterNames()) {
                            final List<StringValue> values = reqPara.getParameterValues(name);
                            final String[] valArray;
                            if (values == null) {
                                valArray = ArrayUtils.EMPTY_STRING_ARRAY;
                            } else {
                                valArray = new String[values.size()];
                                int i = 0;
                                for (final StringValue value : values) {
                                    valArray[i] = value.toString();
                                    i++;
                                }
                            }
                            parameters.put(name, valArray);
                        }

                        Context.begin(this.userName, super.getLocale(), this.sessionAttributes, parameters, null,
                                Context.Inheritance.Inheritable);
                        // set the locale in the context and in the session
                        setLocale(Context.getThreadContext().getLocale());
                        setAttribute(UserAttributesSet.CONTEXTMAPKEY,
                                Context.getThreadContext().getUserAttributes());
                        Context.getThreadContext().setPath(request.getContextPath());
                    }
                }
            } catch (final EFapsException e) {
                EFapsSession.LOG.error("could not initialise the context", e);
                throw new RestartResponseException(new ErrorPage(e));
            }
        }
    }

    /**
     * Method that closes the opened Context {@link #openContext()}, by
     * committing or rollback it.
     *
     * @see #detach()
     */
    public void closeContext() {
        if (isLogedIn()) {
            try {
                if (!Context.isTMNoTransaction()) {
                    if (Context.isTMActive()) {
                        Context.commit();
                    } else {
                        Context.rollback();
                    }
                }
            } catch (final SecurityException e) {
                throw new RestartResponseException(new ErrorPage(e));
            } catch (final IllegalStateException e) {
                throw new RestartResponseException(new ErrorPage(e));
            } catch (final EFapsException e) {
                throw new RestartResponseException(new ErrorPage(e));
            }
        }
    }

    /**
     * Save the Context.
     */
    public void saveContext() {
        closeContext();
        openContext();
    }

    /**
     * @param _file FIle to be used for the ShowFileCallBackBehavior
     */
    public void setFile(final File _file) {
        this.file = _file;
    }

    /**
     * Getter method for instance variable {@link #file}.
     *
     * @return value of instance variable {@link #file}
     */
    public File getFile() {
        return this.file;
    }

    /**
     * @param _embededLink link to add
     */
    public void addEmbededLink(final EmbeddedLink _embededLink) {
        this.embededlinks.add(_embededLink);
    }

    /**
     * Getter method for the instance variable {@link #linkElements}.
     *
     * @return value of instance variable {@link #linkElements}
     */
    public List<EmbeddedLink> getEmbededLinks() {
        return this.embededlinks;
    }

    @Override
    public void onInvalidate() {
        EFapsSession.LOG.trace("Session invalidated: {}", this);
        RegistryManager.removeUserSession(getId());
        super.onInvalidate();
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this).append("userName", this.userName).append("sessionId", getId()).build();
    }

    /**
     * @return the current EFapsSession
     */
    public static EFapsSession get() {
        return (EFapsSession) Session.get();
    }

    /**
     * This Class is used to pass the FileItems along with its Parameters to the
     * Context.
     */
    public static class FileParameter implements Context.FileParameter {

        /**
         * The FileItem of this FileParameter.
         */
        private final FileItem fileItem;

        /**
         * The Name of the Parameter of the FileParameter.
         */
        private final String parameterName;

        /**
         * Constructor setting the Name of the Parameter and the FileItem.
         *
         * @param _parameterName name of the parameter
         * @param _fileItem file item
         */
        public FileParameter(final String _parameterName, final FileItem _fileItem) {
            this.parameterName = _parameterName;
            this.fileItem = _fileItem;
        }

        /**
         * Not needed.
         */
        @Override
        public void close() {
            // not needed yet
        }

        /**
         * Method to get the content type of the fileitem.
         *
         * @return content type
         */
        @Override
        public String getContentType() {
            return this.fileItem.getContentType();
        }

        /**
         * Get the input stream of the fileitem.
         *
         * @return Inputstream
         * @throws IOException on error
         */
        @Override
        public InputStream getInputStream() throws IOException {
            return this.fileItem.getInputStream();
        }

        /**
         * Get the name of the file item.
         *
         * @return name of the file item
         */
        @Override
        public String getName() {
            return this.fileItem.getName();
        }

        /**
         * Get the name of the parameter.
         *
         * @return name of the parameter
         */
        @Override
        public String getParameterName() {
            return this.parameterName;
        }

        /**
         * Get the size of the file item.
         *
         * @return size in byte
         */
        @Override
        public long getSize() {
            return this.fileItem.getSize();
        }
    }
}