Package org.jasig.portal.layout.dlm

Source Code of org.jasig.portal.layout.dlm.FragmentActivator$UserViewKey

/**
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you 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.
*/

package org.jasig.portal.layout.dlm;

import java.io.Serializable;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.regex.Pattern;

import net.sf.ehcache.Ehcache;
import net.sf.ehcache.constructs.blocking.CacheEntryFactory;
import net.sf.ehcache.constructs.blocking.SelfPopulatingCache;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jasig.portal.AuthorizationException;
import org.jasig.portal.IUserIdentityStore;
import org.jasig.portal.IUserProfile;
import org.jasig.portal.UserProfile;
import org.jasig.portal.i18n.LocaleManager;
import org.jasig.portal.layout.IUserLayoutStore;
import org.jasig.portal.properties.PropertiesManager;
import org.jasig.portal.security.IPerson;
import org.jasig.portal.security.provider.PersonImpl;
import org.jasig.portal.utils.Tuple;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;

/**
* @version $Revision$ $Date$
* @since uPortal 2.5
*/
@Service
public class FragmentActivator
{
    private static final String NEWLY_CREATED_ATTR = "newlyCreated";
    public static final String RCS_ID = "@(#) $Header$";
    private static final Log LOG = LogFactory.getLog(FragmentActivator.class);

    private final LoadingCache<String, List<Locale>> fragmentOwnerLocales = CacheBuilder.newBuilder()
            .<String, List<Locale>>build(new CacheLoader<String, List<Locale>>() {
                @Override
                public List<Locale> load(String key) throws Exception {
                    return new CopyOnWriteArrayList<Locale>();
                }
            });

    private Ehcache userViews;
    private Ehcache userViewErrors;
    private IUserIdentityStore identityStore;
    private IUserLayoutStore userLayoutStore;
    private ConfigurationLoader configurationLoader;

    private static final String PROPERTY_ALLOW_EXPANDED_CONTENT = "org.jasig.portal.layout.dlm.allowExpandedContent";
    private static final Pattern STANDARD_PATTERN = Pattern.compile("\\A[Rr][Ee][Gg][Uu][Ll][Aa][Rr]\\z");
    private static final Pattern EXPANDED_PATTERN = Pattern.compile(".*");
   
    @Autowired
    public void setUserViewErrors(@Qualifier("org.jasig.portal.layout.dlm.FragmentActivator.userViewErrors") Ehcache userViewErrors) {
        this.userViewErrors = userViewErrors;
    }

    @Autowired
    public void setUserViews(@Qualifier("org.jasig.portal.layout.dlm.FragmentActivator.userViews") Ehcache userViews) {
        this.userViews = new SelfPopulatingCache(userViews, new CacheEntryFactory() {
            @Override
            public Object createEntry(Object key) throws Exception {
                final UserViewKey userViewKey = (UserViewKey)key;
               
                //Check if there was an exception the last time a load attempt was made and re-throw
                final net.sf.ehcache.Element exceptionElement = userViewErrors.get(userViewKey);
                if (exceptionElement != null) {
                    throw (Exception)exceptionElement.getObjectValue();
                }
               
                try {
                    return activateFragment(userViewKey);
                }
                catch (Exception e) {
                    userViewErrors.put(new net.sf.ehcache.Element(userViewKey, e));
                    throw e;
                }
            }
        });
    }

    @Autowired
    public void setConfigurationLoader(ConfigurationLoader configurationLoader) {
        this.configurationLoader = configurationLoader;
    }

    @Autowired
    public void setIdentityStore(IUserIdentityStore identityStore) {
        this.identityStore = identityStore;
    }

    @Autowired
    public void setUserLayoutStore(IUserLayoutStore userLayoutStore) {
        this.userLayoutStore = userLayoutStore;
    }
   
    private static class UserViewKey implements Serializable {
        private static final long serialVersionUID = 1L;
        private final String ownerId;
        private final Locale locale;
        private final int hashCode;
       
        public UserViewKey(String ownerId, Locale locale) {
            this.ownerId = ownerId;
            this.locale = locale;
            this.hashCode = internalHashCode();
        }
       
        public String getOwnerId() {
            return ownerId;
        }
        public Locale getLocale() {
            return locale;
        }

        @Override
        public int hashCode() {
            return this.hashCode;
        }

        public int internalHashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((ownerId == null) ? 0 : ownerId.hashCode());
            result = prime * result + ((locale == null) ? 0 : locale.hashCode());
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            UserViewKey other = (UserViewKey) obj;
            if (ownerId == null) {
                if (other.ownerId != null)
                    return false;
            }
            else if (!ownerId.equals(other.ownerId))
                return false;
            if (locale == null) {
                if (other.locale != null)
                    return false;
            }
            else if (!locale.equals(other.locale))
                return false;
            return true;
        }

        @Override
        public String toString() {
            return "UserViewKey [ownerId=" + ownerId + ", locale=" + locale + "]";
        }
    }
   
    private UserView activateFragment(final UserViewKey userViewKey) {
        final String ownerId = userViewKey.getOwnerId();
        final FragmentDefinition fd = configurationLoader.getFragmentByOwnerId(ownerId);
       
        final Locale locale = userViewKey.getLocale();
       
        fragmentOwnerLocales.getUnchecked(ownerId).add(locale);
       
        if (fd.isNoAudienceIncluded()) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("Skipping activation of FragmentDefinition " + fd.getName() + ", no evaluators found. " + fd);
            }

            return null;
        }

        if (LOG.isDebugEnabled()) {
            LOG.debug("Activating FragmentDefinition " + fd.getName() + " with locale " + locale);
        }

        IPerson owner = bindToOwner(fd);
        UserView view = new UserView(owner.getID());
        loadLayout(view, fd, owner, locale);

        // if owner just created we need to push the layout into
        // the db so that our fragment template user is used and
        // not the default template user as determined by
        // the user identity store.
        if (owner.getAttribute(NEWLY_CREATED_ATTR) != null) {
            owner.setAttribute(Constants.PLF, view.layout);
            try {
                saveLayout(view, owner);
            }
            catch (Exception e) {
                throw new RuntimeException("Failed to save layout for newly created fragment owner "
                        + owner.getUserName(), e);
            }
        }

        loadPreferences(view, fd);
        fragmentizeLayout(view, fd);
       
        if (LOG.isInfoEnabled()) {
            LOG.info("Activated FragmentDefinition " + fd.getName() + " with locale " + locale);
        }
        return view;
    }
   
    public UserView getUserView(final FragmentDefinition fd, final Locale locale) {
        final UserViewKey userViewKey = new UserViewKey(fd.getOwnerId(), locale);
        final net.sf.ehcache.Element userViewElement = this.userViews.get(userViewKey);
        return (UserView)userViewElement.getObjectValue();
    }
   
    public boolean hasUserView(FragmentDefinition fd, Locale locale) {

        // Assertions...
        if (fd == null) {
            String msg  = "Argument 'fd' [FragmentDefinition] cannot be null.";
            throw new IllegalArgumentException(msg);
        }

        return getUserView(fd.getOwnerId(), locale) != null;

    }
   
    /**
     * Saves the loaded layout in the database for the user and profile.
     * @param view
     * @param owner
     * @throws Exception
     */
    private void saveLayout(UserView view, IPerson owner) throws Exception
    {
        IUserProfile profile = new UserProfile();
        profile.setProfileId(view.profileId);
        userLayoutStore.setUserLayout(owner, profile, view.layout, true, false);
    }

    private IPerson bindToOwner( FragmentDefinition fragment )
    {
        IPerson owner = new PersonImpl();
        owner.setAttribute( "username", fragment.getOwnerId() );
        int userID = -1;
       
        try
        {
            userID = identityStore.getPortalUID( owner, false );
        }
        catch( AuthorizationException ae )
        {
            // current implementation of RDMBUserIdentityStore throws an
            // auth exception if the user doesn't exist even if
            // create data is false as we have it here. So this exception
            // can be discarded since we check for the userID being -1
            // meaning that the user wasn't found to trigger creating
            // that user.
        }
        if (userID == -1)
        {
            userID = createOwner( owner, fragment );
            owner.setAttribute(NEWLY_CREATED_ATTR, "" + (userID != -1));
        }

        owner.setID(userID);
        return owner;
    }
   
    private int createOwner( IPerson owner, FragmentDefinition fragment )
    {
        String defaultUser = null;
        int userID = -1;
           
        if ( fragment.defaultLayoutOwnerID != null ) {
            defaultUser = fragment.defaultLayoutOwnerID;
        }
        else {
            final String defaultLayoutOwner = PropertiesManager.getProperty("org.jasig.portal.layout.dlm.defaultLayoutOwner");
            if ( defaultLayoutOwner != null ) {
                defaultUser = defaultLayoutOwner;
            }
            else {
                try
                {
                    defaultUser = PropertiesManager.getProperty( RDBMDistributedLayoutStore.TEMPLATE_USER_NAME );
                }
                catch( RuntimeException re )
                {
                    throw new RuntimeException(
                            "\n\n WARNING: defaultLayoutOwner is not specified" +
                            " in dlm.xml and no default user is configured for " +
                            "the system. Owner '" + fragment.getOwnerId() + "' for " +
                            "fragment '" + fragment.getName() + "' can not be " +
                            "created. The fragment will not be available for " +
                            "inclusion into user layouts.\n", re );
                }
            }
        }

        if (LOG.isDebugEnabled()) {
            LOG.debug("\n\nOwner '" + fragment.getOwnerId() +
            "' of fragment '" + fragment.getName() +
            "' not found. Creating as copy of '" +
            defaultUser + "'\n" );
        }

        if ( defaultUser != null ) {
            owner.setAttribute( "uPortalTemplateUserName", defaultUser );
        }
       
        try
        {
            userID = identityStore.getPortalUID( owner, true );
        }
        catch( AuthorizationException ae )
        {
            throw new RuntimeException(
                  "\n\nWARNING: Anomaly occurred while creating owner '" +
                  fragment.getOwnerId() + "' of fragment '" + fragment.getName() +
                  "'. The fragment will not be " +
                  "available for inclusion into user layouts.", ae );
        }
        return userID;
    }
    private void loadLayout( UserView view,
                             FragmentDefinition fragment,
                             IPerson owner, Locale locale )
    {
        // if fragment not bound to user can't return any layouts.
        if ( view.getUserId() == -1 )
            return;

        // this area is hacked right now. Time won't permit how to handle
        // matching up multiple profiles for a fragment with an appropriate
        // one for incorporating into a user's layout based on their profile
        // when they log in with a certain user agent. The challenge is
        // being able to match up profiles for a user with those of a
        // fragment. Until this is resolved only one profile will be supported
        // and will have a hard coded id of 1 which is the default for profiles.
        // If anyone changes this user all heck could break loose for dlm. :-(
       
        Document layout = null;

        try
        {
            // fix hard coded 1 later for multiple profiles
            IUserProfile profile = userLayoutStore.getUserProfileByFname(owner, "default");
            profile.setLocaleManager(new LocaleManager(owner, new Locale[] { locale }));
           
            // see if we have structure & theme stylesheets for this user yet.
            // If not then fall back on system's selected stylesheets.
            if (profile.getStructureStylesheetId() == 0 ||
                    profile.getThemeStylesheetId() == 0)
                profile = userLayoutStore.getSystemProfileByFname(profile.getProfileFname());
           
            view.profileId = profile.getProfileId();
            view.profileFname = profile.getProfileFname();
            view.layoutId = profile.getLayoutId();
//            view.structureStylesheetId = profile.getStructureStylesheetId();
//            view.themeStylesheetId = profile.getThemeStylesheetId();
           
            layout = userLayoutStore.getFragmentLayout( owner, profile );
            Element root = layout.getDocumentElement();
            root.setAttribute( Constants.ATT_ID,
                    Constants.FRAGMENT_ID_USER_PREFIX + view.getUserId() +
                    Constants.FRAGMENT_ID_LAYOUT_PREFIX + view.layoutId );
            view.layout = layout;
        }
        catch( Exception e )
        {
            throw new RuntimeException(
                  "Anomaly occurred while loading layout for fragment '" +
                  fragment.getName() +
                  "'. The fragment will not be " +
                  "available for inclusion into user layouts.", e );
        }
    }

    private void loadPreferences( UserView view,
                                  FragmentDefinition fragment )
    {
        // if fragment not bound to user can't return any preferences.
        if ( view.getUserId() == -1 )
            return;

        IPerson p = new PersonImpl();
        p.setID( view.getUserId() );
        p.setAttribute( "username", fragment.getOwnerId() );
    }

    /**
     * Removes unwanted and hidden folders, then changes all node ids to their
     * globally safe incorporated version.
     */
    private void fragmentizeLayout( UserView view,
                            FragmentDefinition fragment )
    {
        // if fragment not bound to user or layout empty due to error, return
        if ( view.getUserId() == -1 ||
             view.layout == null )
            return;

        // Choose what types of content to apply from the fragment
        Pattern contentPattern = STANDARD_PATTERN;  // default
        boolean allowExpandedContent = Boolean.parseBoolean(PropertiesManager.getProperty(PROPERTY_ALLOW_EXPANDED_CONTENT));
        if (allowExpandedContent) {
            contentPattern = EXPANDED_PATTERN;
        }

        // remove all non-regular or hidden top level folders
        // skip root folder that is only child of top level layout element
        Element layout = view.layout.getDocumentElement();
        Element root = (Element) layout.getFirstChild();
        NodeList children = root.getChildNodes();

        // process the children backwards since as we delete some the indices
        // shift around
        for( int i=children.getLength()-1; i>=0; i-- )
        {
            Node node = children.item(i);
            if ( node.getNodeType() == Node.ELEMENT_NODE &&
                 node.getNodeName().equals("folder") )
            {
                Element folder = (Element) node;

                // strip out folder types 'header', 'footer' and regular,
                // hidden folder "User Preferences" since users have their own
                boolean isApplicable = contentPattern.matcher(folder.getAttribute("type")).matches();
                if (!isApplicable || folder.getAttribute("hidden").equals("true")) {
                    try
                    {
                        root.removeChild( folder );
                    }
                    catch( Exception e )
                    {
                        throw new RuntimeException(
                              "Anomaly occurred while stripping out " +
                              " portions of layout for fragment '" +
                              fragment.getName() +
                              "'. The fragment will not be available for " +
                              "inclusion into user layouts.", e );
                    }
                }
            }
        }
        // now re-lable all remaining nodes below root to have a safe system
        // wide id.

        setIdsAndAttribs( layout, layout.getAttribute( Constants.ATT_ID ),
                          "" + fragment.getIndex(),
                          "" + fragment.getPrecedence() );
    }

    /**
     * Recursive method that passes through a layout tree and changes all ids
     * from the regular format of sXX or nXX to the globally safe incorporated
     * id of form uXlXsXX or uXlXnXX indicating the user id and layout id from
     * which this node came.
     */
    private void setIdsAndAttribs( Element parent,
                                   String labelBase,
                                   String index,
                                   String precedence )
    {
        NodeList children = parent.getChildNodes();

        for ( int i=0; i<children.getLength(); i++ )
        {
            if ( children.item(i).getNodeType() == Node.ELEMENT_NODE )
            {
                Element child = (Element) children.item(i);
                String id = child.getAttribute( Constants.ATT_ID );
                if ( ! id.equals( "" ) )
                {
                    String newId = labelBase + id;
                    child.setAttribute( Constants.ATT_ID, newId );
                    child.setIdAttribute(Constants.ATT_ID, true);
                    child.setAttributeNS( Constants.NS_URI,
                                          Constants.ATT_FRAGMENT,
                                          index );
                    child.setAttributeNS( Constants.NS_URI,
                                          Constants.ATT_PRECEDENCE,
                                          precedence );
                    setIdsAndAttribs( child, labelBase, index, precedence );
                }
            }
        }
    }
   
    private net.sf.ehcache.Element getUserView(String ownerId, Locale locale) {
        return userViews.get(new Tuple<String, String>(ownerId, locale.toString()));
    }
   
    public void clearChacheForOwner(final String ownerId) {
        final List<Locale> locales = fragmentOwnerLocales.getIfPresent(ownerId);
        if (locales == null) {
            //Nothing to purge
            return;
        }
       
        for (final Locale locale : locales) {
            final UserViewKey userViewKey = new UserViewKey(ownerId, locale);
            userViews.remove(userViewKey);
        }
    }
}
TOP

Related Classes of org.jasig.portal.layout.dlm.FragmentActivator$UserViewKey

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.