/**
 * 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.profile;

import java.util.Collections;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.jasig.portal.layout.IUserLayoutStore;
import org.jasig.portal.security.IPerson;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * This concrete implementation of {@link IProfileMapper} decorates one or more enclosed profile mapper instances.  It
 * will invoke the enclosed mappers in the order specified.  The first mapper that returns a value is the "winner" --
 * subsequent mappers are not checked.  This implementation also supports a default profile fname;  If none of the
 * enclosed mappers returns a value, the specified default will be returned.
 *
 * Fails gracefully.  This means that if a chained mapper fails,
 * the error will be logged and ignored, with the chaining mapper continuing along the chain
 * looking for a non-failing mapper or falling back on the default if there are no answering mappers, just as if the
 * failing mapper had returned null indicating no opinion rather than throwing.
 *
 * @author Jen Bourey, jennifer.bourey@gmail.com
 */
public final class ChainingProfileMapperImpl implements IProfileMapper {

    protected Logger logger = LoggerFactory.getLogger(getClass());

    private String defaultProfileName = "default";

    private List<IProfileMapper> subMappers = Collections.<IProfileMapper>emptyList();

    @Autowired
    private IUserLayoutStore layoutStore;

    public void setDefaultProfileName(String defaultProfileName) {
        this.defaultProfileName = defaultProfileName;
    }


    public void setSubMappers(List<IProfileMapper> subMappers) {
        // Defensive copy
        this.subMappers = Collections.unmodifiableList(subMappers);
    }


    @Override
    public String getProfileFname(IPerson person, HttpServletRequest request) {

        for (IProfileMapper mapper : subMappers) {
            try {
                final String fname = mapper.getProfileFname(person, request);
                if (fname != null) {
                    logger.debug("Profile mapper {} found profile fname={}", mapper, fname);
                    return fname;
                }
            } catch (final Exception mapperThrownException) {
                logger.error("Profile mapper " + mapper + " threw on attempt to map profile.",
                        mapperThrownException);
                // ignore, treating the mapper as if it has no available opinion about the mapping
            }

        }

        logger.trace("None of the chained profile mappers [{}] mapped to a profile, " +
                "so returning default profile [{}].",
                subMappers, defaultProfileName);

        return defaultProfileName;
    }

}

@@ -903,24 +903,136 @@
 etRequest;%0A%0A
+import org.apache.commons.lang3.builder.ToStringBuilder;%0Aimport org.apache.commons.lang3.builder.ToStringStyle;%0A
 import org.j
@@ -3702,10 +3702,270 @@
 %0A    %7D%0A%0A
+    @Override%0A    public String toString() %7B%0A        return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)%0A            .append(%22subMappers%22, subMappers)%0A            .append(%22defaultProfileName%22, defaultProfileName)%0A            .toString();%0A    %7D%0A%0A
 %7D%0A

Add toString() to ChainingProfileMapperImpl .