/*
 * Copyright (C) 2002-2004 Red Hat Inc. All Rights Reserved.
 *
 * The contents of this file are subject to the CCM Public
 * License (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.redhat.com/licenses/ccmpl.html.
 *
 * Software distributed under the License is distributed on an
 * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
 * or implied. See the License for the specific language
 * governing rights and limitations under the License.
 *
 */
package com.arsdigita.cms;

import com.arsdigita.kernel.Kernel;
import com.arsdigita.kernel.KernelExcursion;
import java.io.IOException;
import javax.servlet.ServletException;
import org.apache.log4j.Logger;

/**
 *
 * A CMS excursion is a way of making your code execute under an
 * alternative environment (context). Override the excurse method to
 * create a CMSExcursion. For example:
 *
 * <pre>
 *      CMSExcursion excursion = new CMSExcursion() {
 *              public void excurse() {
 *                  // Set up specific context variables.
 *                  setContentItem(ItemDispatcher.getContentItem());
 *
 *                  // Execute code in new context.
 *
 *              }};
 *
 *      excursion.run();
 * </pre>
 *
 * @author Daniel Berrange
 * @see com.arsdigita.cms.CMS
 * @see com.arsdigita.kernel.KernelExcursion
 */
public abstract class CMSExcursion {
    public static final String versionId =
        "$Id: //cms/dev/src/com/arsdigita/cms/CMSExcursion.java#10 $" +
        "$Author: dennis $" +
        "$DateTime: 2004/04/07 16:07:11 $";

    private static final Logger s_log = Logger.getLogger(CMSExcursion.class);

    /**
     * Begins execution of the excursion. This locks
     * the pending context, sets it to be the current
     * context and invokes the excuse method. The
     * original context is restored upon termination of
     * the excurse method
     */
    public final void run()
        throws ServletException, IOException {

        s_log.debug("Running excursion");

        CMSContext context = CMS.getContext();

        CMS.setContext(context.copy());

        try {
            final ServletException[] servletException = { null };
            final IOException[] ioException = { null };

            new KernelExcursion() {
                protected final void excurse() {
                    setEffectiveParty(Kernel.getSystemParty());

                    try {
                        CMSExcursion.this.excurse();
                    } catch (ServletException se) {
                        servletException[0] = se;
                    } catch (IOException ioe) {
                        ioException[0] = ioe;
                    }
                }
            }.run();

            if (servletException[0] != null) {
                throw servletException[0];
            }

            if (ioException[0] != null) {
                throw ioException[0];
            }
        } finally {
            CMS.setContext(context);
        }
    }

    /**
     * <p>When this method is called the current CMSContext is
     * copied and the code inside the excurse() method is given the
     * opportunity to modify the new context. Any code then called
     * from within the excurse() method is executed in this new
     * context. After the excurse() method completes the old context
     * is restored exactly as it was before. This makes it unnecessary
     * to write error prone code like this:</p>
     *
     * <blockquote><pre>
     *   ContentItem oldItem = context.getContentItem();
     *   context.setContentItem(newItem);
     *   ...
     *   // do something
     *   ...
     *   context.setCOntentItem(oldItem); // If this is forgotten, bad
     *                               // things can happen.
     * </pre></blockquote>
     */
    protected abstract void excurse() throws ServletException, IOException;

    /**
     * Sets the current content section.
     *
     * @param section the new content section
     */
    protected final void setContentSection(ContentSection section) {
        CMS.getContext().setContentSection(section);
    }

    /**
     * Sets the current content item.
     *
     * @param item the new content item
     */
    protected final void setContentItem(ContentItem item) {
        CMS.getContext().setContentItem(item);
    }

    /**
     * Sets the current security manager.
     *
     * @param security the new <code>SecurityManager</code>
     */
    protected final void setSecurityManager(final SecurityManager security) {
        CMS.getContext().setSecurityManager(security);
    }
}
