001// Copyright 2004, 2005 The Apache Software Foundation
002//
003// Licensed under the Apache License, Version 2.0 (the "License");
004// you may not use this file except in compliance with the License.
005// You may obtain a copy of the License at
006//
007//     http://www.apache.org/licenses/LICENSE-2.0
008//
009// Unless required by applicable law or agreed to in writing, software
010// distributed under the License is distributed on an "AS IS" BASIS,
011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012// See the License for the specific language governing permissions and
013// limitations under the License.
014
015package org.apache.tapestry.engine;
016
017import java.io.IOException;
018import java.util.HashMap;
019import java.util.Map;
020
021import org.apache.hivemind.ApplicationRuntimeException;
022import org.apache.hivemind.util.Defense;
023import org.apache.tapestry.IExternalPage;
024import org.apache.tapestry.IPage;
025import org.apache.tapestry.IRequestCycle;
026import org.apache.tapestry.Tapestry;
027import org.apache.tapestry.services.LinkFactory;
028import org.apache.tapestry.services.ResponseRenderer;
029import org.apache.tapestry.services.ServiceConstants;
030
031/**
032 * The external service enables external applications to reference Tapestry pages via a URL. Pages
033 * which can be referenced by the external service must implement the {@link IExternalPage}
034 * interface. The external service enables the bookmarking of pages.
035 * <p>
036 * The external service may also be used by the Tapestry JSP taglibrary (
037 * {@link org.apache.tapestry.jsp.ExternalURLTag}and {@link org.apache.tapestry.jsp.ExternalTag}).
038 * <p>
039 * You can try and second guess the URL format used by Tapestry. The default URL format for the
040 * external service is: <blockquote>
041 * <tt>http://localhost/app?service=external/<i>[Page Name]</i>&amp;sp=[Param 0]&amp;sp=[Param 1]...</tt>
042 * </blockquote> For example to view the "ViewCustomer" page the service parameters 5056 (customer
043 * ID) and 309 (company ID) the external service URL would be: <blockquote>
044 * <tt>http://localhost/myapp?service=external&amp;context=<b>ViewCustomer</b>&amp;sp=<b>5056</b>&amp;sp=<b>302</b></tt>
045 * </blockquote> In this example external service will get a "ViewCustomer" page and invoke the
046 * {@link IExternalPage#activateExternalPage(Object[], IRequestCycle)}method with the parameters:
047 * Object[] { new Integer(5056), new Integer(302) }.
048 * <p>
049 * Note service parameters (sp) need to be prefixed by valid
050 * {@link org.apache.tapestry.util.io.DataSqueezerImpl}adaptor char. These adaptor chars are
051 * automatically provided in URL's created by the <tt>buildGesture()</tt> method. However if you
052 * hand coded an external service URL you will need to ensure valid prefix chars are present.
053 * <p>
054 * <table border="1" cellpadding="2">
055 * <tr>
056 * <th>Prefix char(s)</th>
057 * <th>Mapped Java Type</th>
058 * </tr>
059 * <tr>
060 * <td>&nbsp;TF</td>
061 * <td>&nbsp;boolean</td>
062 * </tr>
063 * <tr>
064 * <td>&nbsp;b</td>
065 * <td>&nbsp;byte</td>
066 * </tr>
067 * <tr>
068 * <td>&nbsp;c</td>
069 * <td>&nbsp;char</td>
070 * </tr>
071 * <tr>
072 * <td>&nbsp;d</td>
073 * <td>&nbsp;double</td>
074 * </tr>
075 * <tr>
076 * <td>&nbsp;-0123456789</td>
077 * <td>&nbsp;integer</td>
078 * </tr>
079 * <tr>
080 * <td>&nbsp;l</td>
081 * <td>&nbsp;long</td>
082 * </tr>
083 * <tr>
084 * <td>&nbsp;S</td>
085 * <td>&nbsp;String</td>
086 * </tr>
087 * <tr>
088 * <td>&nbsp;s</td>
089 * <td>&nbsp;short</td>
090 * </tr>
091 * <tr>
092 * <td>&nbsp;other chars</td>
093 * <td>&nbsp; <tt>String</tt> without truncation of first char</td>
094 * </tr>
095 * <table>
096 * <p>
097 * <p>
098 * A good rule of thumb is to keep the information encoded in the URL short and simple, and restrict
099 * it to just Strings and Integers. Integers can be encoded as-is. Prefixing all Strings with the
100 * letter 'S' will ensure that they are decoded properly. Again, this is only relevant if an
101 * {@link org.apache.tapestry.IExternalPage}is being referenced from static HTML or JSP and the URL
102 * must be assembled in user code ... when the URL is generated by Tapestry, it is automatically
103 * created with the correct prefixes and encodings (as with any other service).
104 * 
105 * @see org.apache.tapestry.IExternalPage
106 * @see org.apache.tapestry.jsp.ExternalTag
107 * @see org.apache.tapestry.jsp.ExternalURLTag
108 * @author Howard Lewis Ship
109 * @author Malcolm Edgar
110 * @since 2.2
111 */
112
113public class ExternalService implements IEngineService
114{
115    /** @since 4.0 */
116
117    private ResponseRenderer _responseRenderer;
118
119    /** @since 4.0 */
120    private LinkFactory _linkFactory;
121
122    /**
123     * {@inheritDoc}
124     * 
125     * @return The URL for the service. The URL will always be encoded when it is returned.
126     */
127    public ILink getLink(boolean post, Object parameter)
128    {
129        Defense.isAssignable(parameter, ExternalServiceParameter.class, "parameter");
130
131        ExternalServiceParameter esp = (ExternalServiceParameter) parameter;
132
133        Map parameters = new HashMap();
134
135        parameters.put(ServiceConstants.PAGE, esp.getPageName());
136        parameters.put(ServiceConstants.PARAMETER, esp.getServiceParameters());
137
138        return _linkFactory.constructLink(this, post, parameters, true);
139    }
140
141    public void service(IRequestCycle cycle) throws IOException
142    {
143        String pageName = cycle.getParameter(ServiceConstants.PAGE);
144        IPage rawPage = cycle.getPage(pageName);
145
146        IExternalPage page = null;
147
148        try
149        {
150            page = (IExternalPage) rawPage;
151        }
152        catch (ClassCastException ex)
153        {
154            throw new ApplicationRuntimeException(EngineMessages.pageNotCompatible(
155                    rawPage,
156                    IExternalPage.class), rawPage, null, ex);
157        }
158
159        Object[] parameters = _linkFactory.extractListenerParameters(cycle);
160
161        cycle.setListenerParameters(parameters);
162
163        cycle.activate(page);
164
165        page.activateExternalPage(parameters, cycle);
166
167        _responseRenderer.renderResponse(cycle);
168    }
169
170    public String getName()
171    {
172        return Tapestry.EXTERNAL_SERVICE;
173    }
174
175    /** @since 4.0 */
176
177    public void setResponseRenderer(ResponseRenderer responseRenderer)
178    {
179        _responseRenderer = responseRenderer;
180    }
181
182    /** @since 4.0 */
183    public void setLinkFactory(LinkFactory linkFactory)
184    {
185        _linkFactory = linkFactory;
186    }
187}