/*  Copyright (c) 2000 by Oracle Corporation. All Rights Reserved. */
package oracle.portal.provider.v1.http;

import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

import oracle.xml.parser.v2.*;
import oracle.portal.provider.v1.*;
import oracle.webdb.utils.SimpleStringBuffer;

class ServletProviderResponse
{
    private static final String XML_CONTENT = "text/xml;charset=UTF-8";
    // set up by first instance so only use in an instance
    private static String sLineSep = null;

    private ServletProviderRequest mRequest;

    private static final NodeFactory mNodeFactory = new NodeFactory();

    /**
     * Constructor
     */
    ServletProviderResponse(ServletProviderRequest request)
    {
        mRequest = request;

        // Need to know the OS line separator so we don't set HTTP status that
        // are greater then a single line -- I.e. we use this to strip down to
        // a single line.
        if (sLineSep == null)
        {
            sLineSep = System.getProperty( "line.separator");
            if (sLineSep == null) sLineSep = "\n";
        }
    }

    protected void emitHeader()
    {
        HttpServletResponse response = mRequest.getResponse();
        response.setContentType(XML_CONTENT);
    }

    private String translateException(Throwable t)
    {
        if (mRequest.getDebugLevel() > 0)
        {
            CharArrayWriter c = new CharArrayWriter();
            t.printStackTrace(new PrintWriter(c));
            return c.toString();
        }
        else
        {
            return t.getMessage();
        }
    }

    protected void setFault(int faultType, Throwable t)
    {
        SoapWriter sw = new SoapWriter();
        sw.setFault(faultType, t);
        sw.commit();
    }

    protected void setFault(int faultType, String detail)
    {
        SoapWriter sw = new SoapWriter();
        sw.setFault(faultType, detail);
        sw.commit();
    }

    protected void setStatus(int status, Throwable t)
    {
        setStatus(status, translateException(t));
    }

    protected void setStatus(int status, String msg)
    {
        HttpServletResponse response = mRequest.getResponse();

        if (msg != null)
        {
            // Only return to first line separator
            String tempMsg = msg;
            int i = tempMsg.indexOf(sLineSep);
            if (i != -1)
            {
                tempMsg = tempMsg.substring(0, i);
            }
            response.setStatus(status, tempMsg);
        }
        else
        {
            response.setStatus(status);
        }

        if (mRequest.getDebugLevel() > 0  && status > 299)
        {
            mRequest.getServletContext().log("Response status: " + status +
                                             " : " + msg);
        }
    }

    protected void setStatus(int status)
    {
        setStatus(status, (String) null);
    }

    protected void registerProvider(Provider provider)
    {
        PerformanceLogger logger = mRequest.getPerformanceLogger();
        SoapWriter sw = new SoapWriter();
        try
        {
            // Collect stats.
            logger.startProcessing();
            sw.marshalRegisterProvider(provider);
            logger.finishProcessing();
        }

        catch (ThreadDeath td)
        {
            // If the thread has been stopped by the thread monitor/killer, 
            // rethrow the exception to be handled properly.
            throw td;
        }
        catch (Throwable t)
        {
            // 'Roll back' the soap envelope on error
            sw.reset();
            sw.setFault(HttpProviderConstants.SOAP_FAULT_TYPE_CLIENT, t);
        }
        finally
        {
            // Output whatever soap we generated
            sw.commit();
        }
    }

    protected void deregisterProvider(Provider provider)
    {
        PerformanceLogger logger = mRequest.getPerformanceLogger();
        SoapWriter sw = new SoapWriter();
        try
        {
            // Collect stats
            logger.startProcessing();
            provider.deregister();
            logger.finishProcessing();
        }
        catch (ThreadDeath td)
        {
            // If the thread has been stopped by the thread monitor/killer, 
            // rethrow the exception to be handled properly.
            throw td;
        }
        catch (Throwable t)
        {
            // 'Roll back' the soap envelope on error
            sw.reset();
            sw.setFault(HttpProviderConstants.SOAP_FAULT_TYPE_CLIENT, t);
        }
        finally
        {
            // Output whatever soap we generated
            sw.commit();
        }
    }

    protected void getPortlets(Provider provider)
    {
        PerformanceLogger logger = mRequest.getPerformanceLogger();
        SoapWriter sw = new SoapWriter();
        try
        {
            // Time the provider method
            logger.startProcessing();            
            sw.marshalGetPortlets(provider);
            logger.finishProcessing();
        }
        catch (ThreadDeath td)
        {
            // If the thread has been stopped by the thread monitor/killer, 
            // rethrow the exception to be handled properly.
            throw td;
        }
        catch (Throwable t)
        {
            // 'Roll back' the soap envelope on error
            sw.reset();
            sw.setFault(HttpProviderConstants.SOAP_FAULT_TYPE_CLIENT, t);
        }
        finally
        {
            // Output whatever soap we generated
            sw.commit();
        }
    }

    protected void getPortlet(Provider provider)
    {
        PerformanceLogger logger = mRequest.getPerformanceLogger();
        SoapWriter sw = new SoapWriter();
        try
        {
            // Time the provider method
            logger.startProcessing();
            sw.marshalGetPortlet(provider.getPortlet(mRequest.getPortletId(),
                                                  mRequest.getProviderUser()));
            logger.finishProcessing();

        }
        catch (ThreadDeath td)
        {
            // If the thread has been stopped by the thread monitor/killer, 
            // rethrow the exception to be handled properly.
            throw td;
        }
        catch (Throwable t)
        {
            // 'Roll back' the soap envelope on error
            sw.reset();
            sw.setFault(HttpProviderConstants.SOAP_FAULT_TYPE_CLIENT, t);
        }
        finally
        {
            // Output whatever soap we generated
            sw.commit();
        }
    }

    protected void isPortletRunnable(Provider provider)
    {
        PerformanceLogger logger = mRequest.getPerformanceLogger();
        SoapWriter sw = new SoapWriter();
        try
        {
            boolean runnable = true; // the default

            // Time the provider level methods 
            logger.startProcessing();
            Portlet p = provider.getPortlet(mRequest.getPortletId(),
                                            mRequest.getProviderUser());

            if (p != null)
            {
                // check whether is Portlet has a security manager
                PortletSecurityManager securityMgr = p.getSecurityManager();
                if (securityMgr != null)
                {
                    // check whether test is for an instance ot not
                    try
                    {
                        String pName = mRequest.getPortletName();
                        PortletReference ref =
                            new PortletReference(provider, p, pName,
                                                 mRequest.getProviderUser(),
                                                 mRequest.getAcceptLanguage(),
                                                 mRequest.getDefaultLanguage(),
                                                 mRequest.getTranslateMode());
                        runnable =
                            securityMgr.hasAccess(ref,
                                                  mRequest.getProviderUser());
                    }
                    catch (IllegalStateException ise)
                    {
                        // not an instance
                        runnable =
                            securityMgr.hasAccess(p,
                                                  mRequest.getProviderUser());
                    }
                }
            }
            else
            {
                // Not runnable if getPortlet returns null
                runnable = false;
            }
            // Stop the logging timer.
            logger.finishProcessing();
            
            sw.marshalIsPortletRunnable(runnable);
        }
        catch (ThreadDeath td)
        {
            // If the thread has been stopped by the thread monitor/killer, 
            // rethrow the exception to be handled properly.
            throw td;
        }
        catch (Throwable t)
        {
            // 'Roll back' the soap envelope on error
            sw.reset();
            sw.setFault(HttpProviderConstants.SOAP_FAULT_TYPE_CLIENT, t);
        }
        finally
        {
            // Output whatever soap we generated
            sw.commit();
        }
    }

    protected void getAPIVersion(Provider provider)
    {
        PerformanceLogger logger = mRequest.getPerformanceLogger();
        SoapWriter sw = new SoapWriter();
        try
        {
            // Time the provider method
            logger.startProcessing();
            sw.marshalGetAPIVersion(HttpProviderConstants.PROVIDER_API_VERSION);
            logger.finishProcessing();
        }
        catch (ThreadDeath td)
        {
            // If the thread has been stopped by the thread monitor/killer, 
            // rethrow the exception to be handled properly.
            throw td;
        }
        catch (Throwable t)
        {
            // 'Roll back' the soap envelope on error
            sw.reset();
            sw.setFault(HttpProviderConstants.SOAP_FAULT_TYPE_CLIENT, t);
        }
        finally
        {
            // Output whatever soap we generated
            sw.commit();
        }
    }

    protected void registerPortlet(Provider provider)
    {
        PerformanceLogger logger = mRequest.getPerformanceLogger();
        SoapWriter sw = new SoapWriter();
        try
        {
            // Time the provider methods for registering a portlet.            
            logger.startProcessing();
            Portlet p = provider.getPortlet(mRequest.getPortletId(),
                                            mRequest.getProviderUser());

            if (p == null) {
                sw.setFault(HttpProviderConstants.SOAP_FAULT_TYPE_CLIENT,
                         "registerPortlet failed:  user is not authorized to use this portlet.");
                return;
            }

            PortletPersonalizationManager pMgr = p.getPersonalizationManager();

            // check to see if there is a personalization manager
            if (pMgr != null)
            {
                // create the default customization record.
                // Note: current locale is set to default locale so PersonalizationManager can detect this the default language default.
                PortletReference ref =
                    new PortletReference(provider, p, mRequest.getPortletName(),
                                         null, mRequest.getDefaultLanguage(),
                                         mRequest.getDefaultLanguage(), false);
                pMgr.create(ref, mRequest.getProviderUser());
            }
            // Stop the logging timer.
            logger.finishProcessing();
        }
        catch (ThreadDeath td)
        {
            // If the thread has been stopped by the thread monitor/killer, 
            // rethrow the exception to be handled properly.
            throw td;
        }
        catch (Throwable t)
        {
            // 'Roll back' the soap envelope on error
            sw.reset();
            sw.setFault(HttpProviderConstants.SOAP_FAULT_TYPE_CLIENT, t);
        }
        finally
        {
            // Output whatever soap we generated
            sw.commit();
        }
    }

    protected void deregisterPortlet(Provider provider)
    {
        PerformanceLogger logger = mRequest.getPerformanceLogger();
        SoapWriter sw = new SoapWriter();
        try
        {
            // Time the provider methods for deregistering a portlet
            logger.startProcessing();            
            Portlet p = provider.getPortlet(mRequest.getPortletId(),
                                            mRequest.getProviderUser());
            if (p == null)
            {
                sw.setFault(HttpProviderConstants.SOAP_FAULT_TYPE_CLIENT,
                         "deregisterProvider failed:  user is not authorized to use this portlet.");
                return;
            }

            PortletPersonalizationManager pMgr = p.getPersonalizationManager();

            // check to see if there is a personalization manager
            if (pMgr != null)
            {
                // destroy the default customization record. This causes each
                // user customization to also be destroyed.
                PortletReference ref =
                    new PortletReference(provider, p, mRequest.getPortletName(),
                                         null, mRequest.getDefaultLanguage(),
                                         mRequest.getDefaultLanguage(), false);
                pMgr.destroy(ref, mRequest.getProviderUser());
            }
            // Stop the logging timer.
            logger.finishProcessing();
        }
        catch (ThreadDeath td)
        {
            // If the thread has been stopped by the thread monitor/killer, 
            // rethrow the exception to be handled properly.
            throw td;
        }
        catch (Throwable t)
        {
            // 'Roll back' the soap envelope on error
            sw.reset();
            sw.setFault(HttpProviderConstants.SOAP_FAULT_TYPE_CLIENT, t);
        }
        finally
        {
            // Output whatever soap we generated
            sw.commit();
        }
    }

    protected void copyPortlet(Provider provider)
    {
        PerformanceLogger logger = mRequest.getPerformanceLogger();
        SoapWriter sw = new SoapWriter();
        try
        {
            // Time the provider methods for copying a portlet
            logger.startProcessing();
            Portlet p = provider.getPortlet(mRequest.getCopyFromPortletId(),
                                            mRequest.getProviderUser());

            if (p == null)
            {
                sw.setFault(HttpProviderConstants.SOAP_FAULT_TYPE_CLIENT,
                            "copyPortlet failed:  user is not authorized to use this portlet.");
                return;
            }

            PortletPersonalizationManager pMgr = p.getPersonalizationManager();

            // check to see if there is a personalization manager
            if (pMgr != null)
            {
                PortletReference fromRef =
                    new PortletReference(provider, p,
                                         mRequest.getCopyFromPortletName(), null,
                                         mRequest.getDefaultLanguage(),
                                         mRequest.getDefaultLanguage(), false);
                PortletReference toRef =
                    new PortletReference(provider, p,
                                         mRequest.getCopyToPortletName(), null,
                                         mRequest.getDefaultLanguage(),
                                         mRequest.getDefaultLanguage(), false);
                pMgr.copy(fromRef, toRef, mRequest.getProviderUser());
            }
            // Stop the logging timer
            logger.finishProcessing();
        }
        catch (ThreadDeath td)
        {
            // If the thread has been stopped by the thread monitor/killer, 
            // rethrow the exception to be handled properly.
            throw td;
        }
        catch (Throwable t)
        {
            // 'Roll back' the soap envelope on error
            sw.reset();
            sw.setFault(HttpProviderConstants.SOAP_FAULT_TYPE_CLIENT, t);
        }
        finally
        {
            // Output whatever soap we generated
            sw.commit();
        }
    }

    protected void initSession(Provider provider)
    {
        PerformanceLogger logger = mRequest.getPerformanceLogger();
        Cookie[] c = null;
        SoapWriter sw = new SoapWriter();
        try
        {
            try
            {
                // Time the provider initSession call.
                logger.startProcessing();                
                c = (Cookie[])provider.initSession(mRequest.getProviderUser(),
                                                   mRequest.getExternalPrincipal());
                logger.finishProcessing();
            }
            catch (Exception e)
            {
                // we added the above form of initSession for the JPDK that
                // corresponds to 3.0.8 (1.3?)  If the provider doesn't
                // implement this assume its an old provider.  Call it using
                // the deprecated API.
                if (e instanceof NoSuchMethodException)
                {
                    // Time the old style initSession method
                    logger.startProcessing();
                    c = (Cookie[]) provider.initSession(mRequest.getProviderUser());
                    logger.finishProcessing();
                }
                else
                {
                    // catch and handle in outer block
                    throw e;
                }
            }
            if (c != null)
            {
                HttpServletResponse response = mRequest.getResponse();
                for (int i = 0; i < c.length; i++)
                {
                    response.addCookie(c[i]);
                }
            }

            // return the session timeout if it exists
            ProviderSession session =
                mRequest.getProviderUser().getSession(false);
            long timeout = 0;
            if (session != null)
            {
                timeout = session.getMaxInactiveInterval();
            }

            sw.marshalInitSession(timeout);

        }
        catch (AuthenticationException ae)
        {
            // 'Roll back' the soap envelope on error
            sw.reset();
            // set the SOAP fault for authentication failure
            sw.setFault(HttpProviderConstants.SOAP_FAULT_TYPE_AUTH_FAILURE, ae);
        }
        catch (ThreadDeath td)
        {
            // If the thread has been stopped by the thread monitor/killer, 
            // rethrow the exception to be handled properly.
            throw td;
        }
        catch (Throwable t)
        {
            // 'Roll back' the soap envelope on error
            sw.reset();
            sw.setFault(HttpProviderConstants.SOAP_FAULT_TYPE_CLIENT, t);
        }
        finally
        {
            // Output whatever soap we generated
            sw.commit();
        }
    }

    protected void showPortlet(Provider provider)
    {
        try
        {
            ProviderUser user = mRequest.getProviderUser();
            PortletReference ref = null;

            Portlet portlet = provider.getPortlet(mRequest.getPortletId(), user);
            if (portlet == null)
            {
                // The portlet is not accessible to this user.  Display the
                // error message as a comment in the HTML page instead of
                // returning an Internal Server Error
                SimpleStringBuffer buf = new SimpleStringBuffer(200);
                buf.append("<!-- showPortlet failed. User ");
                buf.append(user.getName());
                buf.append(" is not authorized to use this portlet. -->");
                if (mRequest.getDebugLevel() > 0)
                {
                    provider.log(buf.toString());
                }

                HttpServletResponse response = mRequest.getResponse();
                response.setContentType("text/html");
                PrintWriter p = response.getWriter();
                p.print(buf.toString());
                return;
            }
            if (mRequest.getMode() == PortletRenderer.MODE_EDIT_DEFAULTS)
            {
                if (mRequest.getTranslateMode())
                {
                    // translate language is the current language
                    ref = new PortletReference(provider, portlet,
                                               mRequest.getPortletName(), null,
                                               mRequest.getAcceptLanguage(),
                                               mRequest.getDefaultLanguage(),
                                               mRequest.getTranslateMode());
                }
                else
                {
                    // signify the default by setting the current language to
                    // the default
                    ref = new PortletReference(provider, portlet,
                                               mRequest.getPortletName(), null,
                                               mRequest.getAcceptLanguage(),
                                               mRequest.getDefaultLanguage(),
                                               mRequest.getTranslateMode());
                }
            }
            else
            {
                // pass current language in case we need to read/use defaults
                ref = new PortletReference(provider, portlet,
                                           mRequest.getPortletName(), user,
                                           mRequest.getAcceptLanguage(),
                                           mRequest.getDefaultLanguage(),
                                           mRequest.getTranslateMode());
            }
            PortletRenderRequest request =
                new ServletPortletRenderRequest(ref, mRequest);

            // Record the time taken for the portlet to be rendered.
            PerformanceLogger logger = mRequest.getPerformanceLogger();
            logger.startProcessing();
            portlet.getRenderer().render(request);
            logger.finishProcessing();

        }
        catch (ThreadDeath td)
        {
            // This will be caused by the provider thread monitor stopping
            // a hanging portet request forcefully. This is rethrown to be
            // handled properly by the servlet container.
            throw td;
        }
        catch (Throwable t)
        {
            setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, t);
            return;
        }
    }

    // Get around the fact that we can't reset our Servlet 2.0 output buffer
    protected class SoapWriter
    {
        private CharArrayWriter mOutBuff = new CharArrayWriter();
        private PrintWriter mOut = new PrintWriter(mOutBuff);

        private void commit()
        {
            try
            {
                // If we have some SOAP, set the content type and write it
                if(mOutBuff.size() > 0)
                {
                    emitHeader();
                    mRequest.getResponse().getWriter().print(mOutBuff.toString());
                }
                // Otherwise, signal that we have no content
                else
                {
                    setStatus(HttpServletResponse.SC_NO_CONTENT);
                }
            }
            catch (IOException ioe)
            {
                // If we can't write to the response, we can't even send a SOAP
                // fault in this case
                ioe.printStackTrace(System.err);
            }
            finally
            {
                close();
            }
        }

        private void reset()
        {
            mOut.close();
            mOutBuff.reset();
            mOut = new PrintWriter(mOutBuff);
        }

        private void close()
        {
            mOut.close();
            mOutBuff.close();
        }

        protected void setFault(int faultType, Throwable t)
        {
            setFault(faultType, translateException(t));
        }

        protected void setFault(int faultType, String detail)
        {
            //setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, detail);
            setStatus(HttpServletResponse.SC_OK, detail);

            try
            {
                int i = emitEnvelopeHead(HttpProviderConstants.SOAP_FAULT);
                String code = null, msg = null;

                switch  (faultType)
                {
                case HttpProviderConstants.SOAP_FAULT_TYPE_VERSION_MISMATCH:
                    code = HttpProviderConstants.SOAP_FAULT_VERSION_MISMATCH;
                    msg = HttpProviderConstants.SOAP_FAULT_VERSION_MISMATCH_MSG;
                    break;
                case HttpProviderConstants.SOAP_FAULT_TYPE_MUST_UNDERSTAND:
                    code = HttpProviderConstants.SOAP_FAULT_MUST_UNDERSTAND;
                    msg = HttpProviderConstants.SOAP_FAULT_MUST_UNDERSTAND_MSG;
                    break;
                case HttpProviderConstants.SOAP_FAULT_TYPE_SERVER:
                    code = HttpProviderConstants.SOAP_FAULT_SERVER;
                    msg = HttpProviderConstants.SOAP_FAULT_SERVER_MSG;
                    break;
                case HttpProviderConstants.SOAP_FAULT_TYPE_AUTH_FAILURE:
                    code = HttpProviderConstants.SOAP_FAULT_AUTH_FAILURE;
                    msg = HttpProviderConstants.SOAP_FAULT_AUTH_FAILURE_MSG;
                    break;
                default:
                    code = HttpProviderConstants.SOAP_FAULT_CLIENT;
                    msg = HttpProviderConstants.SOAP_FAULT_CLIENT_MSG;
                    break;
                }

                emitString(HttpProviderConstants.SOAP_FAULT_CODE, code, i);
                emitString(HttpProviderConstants.SOAP_FAULT_STRING, msg, i);
                emitString(HttpProviderConstants.SOAP_FAULT_DETAIL, detail, i);

                emitEnvelopeTail(HttpProviderConstants.SOAP_FAULT);
            }
            catch (Exception e)
            {
                // swallow and continue
            }
        }
        private void marshalRegisterProvider(Provider p)
            throws IOException, ProviderException
        {
            int i =
                emitEnvelopeHead(HttpProviderConstants.ACTIONNAME_REGISTER_PROVIDER);
            marshalSubscriber(p.register(mRequest.getSubscriberId(),
                                         mRequest.getPortalVersion()), i);
            emitEnvelopeTail(HttpProviderConstants.ACTIONNAME_REGISTER_PROVIDER);
        }

        private void marshalSubscriber(ProviderSubscriber s, int indent)
            throws IOException, ProviderException
        {
            emitString(HttpProviderConstants.SUBSCRIBER_ID_TAG,
                       (s != null)? s.getId() : null, indent + 1);
            emitString(HttpProviderConstants.SUBSCRIBER_KEY_TAG,
                       (s != null) ? s.getEncryptionKey() : null, indent + 1);
        }

        private void marshalGetPortlets(Provider p)
            throws IOException, ProviderException
        {
            int i = emitEnvelopeHead(HttpProviderConstants.ACTIONNAME_GET_PORTLETS);
            marshalPortletInfos(p.getPortlets(mRequest.getStart() - 1,
                                              mRequest.getCount(),
                                              mRequest.isByUser(),
                                              mRequest.getProviderUser()), i);
            emitEnvelopeTail(HttpProviderConstants.ACTIONNAME_GET_PORTLETS);
        }

        private void marshalPortletInfos(Portlet[] portlets, int indent)
            throws IOException
        {
            indent = indent + 1;

            if (portlets == null || portlets.length == 0)
            {
                emitString(HttpProviderConstants.PORTLET_INFOLIST_TAG, null, indent);
            }
            else
            {
                emitArrayHead(HttpProviderConstants.PORTLET_INFOLIST_TAG,
                              HttpProviderConstants.ARRAY_TYPE_PORTLET_INFOS,
                              portlets.length, indent);
                for (int i = 0; i < portlets.length; i++)
                {
                    marshalPortletInfo(portlets[i], indent);
                }
                emitArrayTail(HttpProviderConstants.PORTLET_INFOLIST_TAG, indent);
            }
        }

        private void marshalGetPortlet(Portlet portlet) throws IOException
        {

            int i = emitEnvelopeHead(HttpProviderConstants.ACTIONNAME_GET_PORTLET);
            if (portlet == null)
            {
                // output a null response
                emitString(HttpProviderConstants.PORTLET_INFO_TAG, null, i + 1);
            }
            else
            {
                marshalPortletInfo(portlet, i + 1);
            }
            emitEnvelopeTail(HttpProviderConstants.ACTIONNAME_GET_PORTLET);
        }

        private void marshalPortletInfo(Portlet portlet, int indent) throws IOException
        {
            int i = indent + 2;
            Locale l = mRequest.getLanguage();

            emitTagHead(HttpProviderConstants.PORTLET_INFO_TAG, indent + 1);

            emitLong(HttpProviderConstants.PORTLET_ID_TAG, portlet.getId(), i);
            emitLong(HttpProviderConstants.PROVIDER_ID_TAG,
                     portlet.getProvider().getProviderId(), i);
            emitString(HttpProviderConstants.PORTLET_NAME_TAG, portlet.getName(l),
                       i);
            emitString(HttpProviderConstants.PORTLET_TITLE_TAG, portlet.getTitle(l),
                       i);
            emitString(HttpProviderConstants.PORTLET_DESCRIPTION_TAG,
                       portlet.getDescription(l), i);
            // Next three are currently unused -- we removed them from the Portlet
            // API Hence just set to null.
            emitString(HttpProviderConstants.PORTLET_IMAGEURL_TAG, null, i);
            emitString(HttpProviderConstants.PORTLET_THUMBNAILURL_TAG, null, i);
            emitString(HttpProviderConstants.PORTLET_HELPURL_TAG, null, i);
            emitInt(HttpProviderConstants.PORTLET_TIMEOUT_TAG, portlet.getTimeout(),
                    i);
            emitString(HttpProviderConstants.PORTLET_TIMEOUT_MSG_TAG,
                       portlet.getTimeoutMessage(l), i);
            marshalAcceptContentTypes(portlet, i);
            marshalInputParameters(portlet, i);
            emitBoolean(HttpProviderConstants.PORTLET_HAS_SHOW_EDIT_TAG,
                        portlet.hasShowEdit(), i);
            emitBoolean(HttpProviderConstants.PORTLET_HAS_SHOW_EDIT_DEFAULT_TAG,
                        portlet.hasShowEditDefault(), i);
            emitBoolean(HttpProviderConstants.PORTLET_HAS_SHOW_PREVIEW_TAG,
                        portlet.hasShowPreview(), i);
            // Next four are currently unused -- we removed them from the Portlet
            // API Hence just set to null.
            emitString(HttpProviderConstants.PORTLET_CREATE_DATE_TAG, null, i);
            emitString(HttpProviderConstants.PORTLET_CREATE_BY_TAG, null, i);
            emitString(HttpProviderConstants.PORTLET_UPDATE_DATE_TAG, null, i);
            emitString(HttpProviderConstants.PORTLET_UPDATE_BY_TAG, null, i);
            // Next three added for mobile support
            if (portlet instanceof MobilePortlet)
            {
                emitBoolean(HttpProviderConstants.PORTLET_HAS_SHOW_LINK_TAG,
                            ((MobilePortlet)portlet).hasShowLinkMode(), i);
                emitBoolean(HttpProviderConstants.PORTLET_CALL_IS_RUNNABLE_TAG,
                            ((MobilePortlet)portlet).callIsRunnable(), i);
                emitBoolean(HttpProviderConstants.PORTLET_CALL_GET_PORTLET_TAG,
                            ((MobilePortlet)portlet).callGetPortlet(), i);
            }
            else  // emit the default values
            {
                emitBoolean(HttpProviderConstants.PORTLET_HAS_SHOW_LINK_TAG,
                            false, i);
                emitBoolean(HttpProviderConstants.PORTLET_CALL_IS_RUNNABLE_TAG,
                            true, i);
                emitBoolean(HttpProviderConstants.PORTLET_CALL_GET_PORTLET_TAG,
                            true, i);
            }

            // Second extension to the Portlet interface
            if (portlet instanceof PortletXtra2)
            {
                emitLong(HttpProviderConstants.PORTLET_MOBILE_FLAGS_TAG,
                            ((PortletXtra2)portlet).getMobileFlags().getValue(), i);
                emitString(HttpProviderConstants.PORTLET_SHORT_TITLE_TAG,
                            ((PortletXtra2)portlet).getShortTitle(l), i);
            }
            else   // emit the default values
            {
                emitLong(HttpProviderConstants.PORTLET_MOBILE_FLAGS_TAG,
                            MobileFlags.MOBILE_GENERIC, i);
                emitString(HttpProviderConstants.PORTLET_SHORT_TITLE_TAG,
                            null, i);
            }

            emitTagTail(HttpProviderConstants.PORTLET_INFO_TAG, indent + 1);

        }

        private void marshalAcceptContentTypes(Portlet portlet, int indent)
            throws IOException
        {
            String[] cTypes = portlet.getAcceptContentTypes();

            // default to text/html
            if (cTypes == null || cTypes.length == 0)
            {
                cTypes = new String[1];
                cTypes[0] = "text/html";
            }

            emitArrayHead(HttpProviderConstants.PORTLET_ACCEPT_CONTENT_TYPES_TAG,
                          HttpProviderConstants.ARRAY_TYPE_STRING, cTypes.length,
                          indent);
            for (int i = 0; i < cTypes.length; i++)
            {
                emitString(HttpProviderConstants.PORTLET_CTYPES_ITEM_TAG, cTypes[i],
                           indent + 1);
            }
            emitArrayTail(HttpProviderConstants.PORTLET_ACCEPT_CONTENT_TYPES_TAG,
                          indent);
        }

        private void marshalInputParameters(Portlet portlet, int indent)
            throws IOException
        {
            PortletParameter[] params = portlet.getInputParameters();

            // default to text/html
            if (params == null || params.length == 0)
            {
                emitString(HttpProviderConstants.PORTLET_PARAMETERS_TAG, null,
                           indent);
            }
            else
            {
                emitArrayHead(HttpProviderConstants.PORTLET_PARAMETERS_TAG,
                              HttpProviderConstants.ARRAY_TYPE_PARAMETERS,
                              params.length, indent);
                for (int i = 0; i < params.length; i++)
                {
                    marshalInputParameter(params[i], indent);
                }
                emitArrayTail(HttpProviderConstants.PORTLET_PARAMETERS_TAG, indent);
            }
        }

        private void marshalInputParameter(PortletParameter p, int indent)
            throws IOException
        {
            emitTagHead(HttpProviderConstants.PORTLET_PARAMETER_TAG, indent + 1);
            emitString(HttpProviderConstants.PORTLET_PARAMETER_NAME_TAG,
                       p.getName(), indent + 2);
            emitString(HttpProviderConstants.PORTLET_PARAMETER_TYPE_TAG,
                       PortletParameter.getTypeName(p.getType()), indent + 2);
            emitString(HttpProviderConstants.PORTLET_PARAMETER_DESCRIPTION_TAG,
                       p.getDescription(), indent + 2);
            emitTagTail(HttpProviderConstants.PORTLET_PARAMETER_TAG, indent + 1);
        }

        private void marshalIsPortletRunnable(boolean isRunnable)
            throws IOException
        {
            int i =
                emitEnvelopeHead(HttpProviderConstants.ACTIONNAME_IS_PORTLET_RUNNABLE);
            emitBoolean(HttpProviderConstants.IS_RUNNABLE_TAG, isRunnable, i + 1);
            emitEnvelopeTail(HttpProviderConstants.ACTIONNAME_IS_PORTLET_RUNNABLE);
        }

        private void marshalGetAPIVersion(String version) throws IOException
        {
            int i =
                emitEnvelopeHead(HttpProviderConstants.ACTIONNAME_GET_API_VERSION);
            emitString(HttpProviderConstants.VERSION_TAG, version, i + 1);
            emitEnvelopeTail(HttpProviderConstants.ACTIONNAME_GET_API_VERSION);
        }

        private void marshalInitSession(long timeout) throws IOException
        {
            int i = emitEnvelopeHead(HttpProviderConstants.ACTIONNAME_INIT_SESSION);
            if (timeout > 0)
            {
                emitLong(HttpProviderConstants.TIMEOUT_TAG, timeout, i + 1);
            }
            else
            {
                // no timeout -- return null
                emitString(HttpProviderConstants.TIMEOUT_TAG, null, i + 1);
            }
            emitEnvelopeTail(HttpProviderConstants.ACTIONNAME_INIT_SESSION);
        }


        private int emitEnvelopeHead(String tagName) throws IOException
        {
            mOut.println("<SOAP-ENV:Envelope");
            mOut.println("\txmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"");
            mOut.println("\txmlns:xsi=\"http://www.w3.org/1999/XMLSchema-instance\"");
            if (tagName.equals(HttpProviderConstants.SOAP_FAULT))
            {
                //decalre the portal namespace here for a SOAPFault
                mOut.println("\txmlns:portal=\"http://www.oracle.com/portal\"");
            }
            mOut.println("\tSOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/ http://www.oracle.com/portal/encoding/\" >");
            mOut.println("\n\t<SOAP-ENV:Body>");
            if (!tagName.equals(HttpProviderConstants.SOAP_FAULT))
            {
                mOut.println("\t\t<portal:"+ tagName + "Response xmlns:portal=\"http://www.oracle.com/portal\" >");
            }
            else
            {
                emitTagHead(HttpProviderConstants.SOAP_FAULT, 2);
            }
            return 2; // indent twice
        }

        private void emitEnvelopeTail(String tagName) throws IOException
        {
            if (!tagName.equals(HttpProviderConstants.SOAP_FAULT))
            {
                mOut.println("\t\t</portal:" + tagName + "Response >");
            }
            else
            {
                emitTagTail(HttpProviderConstants.SOAP_FAULT, 2);
            }

            mOut.println("\t</SOAP-ENV:Body>");
            mOut.println("</SOAP-ENV:Envelope>");
        }

        private void emitArrayHead(String tagName, String arrayType, int count,
                                   int indent) throws IOException
        {
            for (int i = 0; i < indent; i++)
            {
                mOut.print("\t");
            }

            mOut.println("<" + tagName + " SOAP-ENV:arrayType=\"" + arrayType + "[" +
                        String.valueOf(count) + "]\" >");

        }

        private void emitArrayTail(String tagName, int indent) throws IOException
        {
            emitTagTail(tagName, indent);
        }

        private void emitTagHead(String tagName, int indent) throws IOException
        {
            for (int i = 0; i < indent; i++) {
                mOut.print("\t");
            }

            mOut.println("<" + tagName + ">");
        }


        private void emitTagTail(String tagName, int indent) throws IOException
        {
            for (int i = 0; i < indent; i++)
            {
                mOut.print("\t");
            }

            mOut.println("</" + tagName + ">");
        }


        private void emitString(String tagName, String value, int indent)
            throws IOException
        {
            for (int i = 0; i < indent; i++)
            {
                mOut.print("\t");
            }

            if (value != null && value.length() > 0)
            {
                // Use the Oracle XML writer to make sure we properly escape
                // characters inside an element
                XMLText textNode = mNodeFactory.createTextNode(value);
                mOut.print("<" + tagName + ">");
                textNode.print(mOut);
                mOut.println("</" + tagName + ">");
            }
            else
            {
                mOut.println("<" + tagName + " xsi:null=\"true\" />");
            }
        }

        private void emitBoolean(String tagName, boolean value, int indent)
            throws IOException
        {
            emitString(tagName, String.valueOf(value), indent);
        }

        private void emitLong(String tagName, long value, int indent)
            throws IOException
        {
            emitString(tagName, String.valueOf(value), indent);
        }

        private void emitInt(String tagName, int value, int indent)
            throws IOException
        {
            emitString(tagName, String.valueOf(value), indent);
        }

        private void emitDate(String tagName, Calendar value, int indent)
            throws IOException
        {
            if (value != null)
            {
                String year = String.valueOf(value.get(Calendar.YEAR));
                String month = String.valueOf(value.get(Calendar.MONTH) + 1);
                String day = String.valueOf(value.get(Calendar.DAY_OF_MONTH));

                if (month.length() == 1) month = "0"+ month;
                if (day.length() == 1) day = "0"+ day;

                emitString(tagName, year + "-" + month + "-" + day, indent);
            }
            else
            {
                emitString(tagName, null, indent);
            }
        }

    }
}
