001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket.markup.html.include;
018
019import java.net.MalformedURLException;
020import java.net.URL;
021import java.nio.charset.Charset;
022
023import javax.servlet.ServletContext;
024
025import org.apache.wicket.IGenericComponent;
026import org.apache.wicket.WicketRuntimeException;
027import org.apache.wicket.core.util.resource.UrlResourceStream;
028import org.apache.wicket.markup.ComponentTag;
029import org.apache.wicket.markup.MarkupStream;
030import org.apache.wicket.markup.html.WebComponent;
031import org.apache.wicket.model.IModel;
032import org.apache.wicket.model.Model;
033import org.apache.wicket.request.UrlUtils;
034import org.apache.wicket.resource.ResourceUtil;
035import org.apache.wicket.util.lang.Args;
036
037
038/**
039 * <p>
040 * Component that includes/ renders the import result of an URL, much like JSP include.
041 * </p>
042 * <p>
043 * Use this to integrate non-Wicket locations in your page. <strong>This component is NOT meant for
044 * integrating more Wicket sources as a means of quick and dirty page composition. Use Panels,
045 * Borders and (Markup)inheritance for page composition instead.</strong>
046 * </p>
047 * <p>
048 * You can feed this component the URL directly, or use a model that should deliver a valid URL. You
049 * can both use absolute (e.g. http://www.theserverside.com/) and relative (e.g. mydir/mypage.html)
050 * urls. This component will try to resolve relative urls to resources in the same webapplication.
051 * </p>
052 * <p>
053 * The following example shows how to integrate a header and footer, coming from a plain HTML source
054 * on the same server is integrated using this component. The files footer.html and header.html
055 * would be located in the web application root directory
056 * </p>
057 * <p>
058 * Java:
059 * 
060 * <pre>
061 *   ...
062 *      add(new Include(&quot;header&quot;, &quot;header.html&quot;));
063 *      add(new Include(&quot;footer&quot;, &quot;footer.html&quot;));
064 *   ...
065 * </pre>
066 * 
067 * Html:
068 * 
069 * <pre>
070 *   ...
071 *      &lt;div&gt;
072 *       &lt;div wicket:id=&quot;header&quot;&gt;header comes here&lt;/div&gt;
073 *       &lt;div&gt;I am the body!&lt;/div&gt;
074 *       &lt;div wicket:id=&quot;footer&quot;&gt;footer comes here&lt;/div&gt;
075 *      &lt;/div&gt;
076 *   ...
077 * </pre>
078 * 
079 * </p>
080 * 
081 * @author Eelco Hillenius
082 */
083public class Include extends WebComponent implements IGenericComponent<String, Include>
084{
085        private static final long serialVersionUID = 1L;
086
087        /**
088         * Construct.
089         * 
090         * @param id
091         *            component id
092         */
093        public Include(final String id)
094        {
095                super(id);
096        }
097
098        /**
099         * Construct.
100         * 
101         * @param id
102         *            component id
103         * @param model
104         *            the model
105         */
106        public Include(String id, IModel<String> model)
107        {
108                super(id, model);
109        }
110
111        /**
112         * Construct.
113         * 
114         * @param id
115         *            component id
116         * @param modelObject
117         *            the model object (will be wrapped in a model)
118         */
119        public Include(String id, String modelObject)
120        {
121                super(id, new Model<>(modelObject));
122        }
123
124        /**
125         * Imports the contents of the url of the model object.
126         * 
127         * @return the imported contents
128         */
129        protected String importAsString()
130        {
131                // gets the model object: should provide us with either an absolute or a
132                // relative url
133                String url = getModelObject();
134
135                if (UrlUtils.isRelative(url))
136                {
137                        return importRelativeUrl(url);
138                }
139                else
140                {
141                        return importAbsoluteUrl(url);
142                }
143        }
144
145        @Override
146        public void onComponentTagBody(final MarkupStream markupStream, final ComponentTag openTag)
147        {
148                String content = importAsString();
149                replaceComponentTagBody(markupStream, openTag, content);
150        }
151
152        /**
153         * Imports from a relative url.
154         * 
155         * @param url
156         *            the url to import
157         * @return the imported url's contents
158         */
159        private String importRelativeUrl(String url)
160        {
161                Args.notEmpty(url, "url");
162
163                if (url.charAt(0) != '/')
164                {
165                        url = '/' + url;
166                }
167
168                try
169                {
170                        ServletContext servletContext = getWebApplication().getServletContext();
171                        URL resource = servletContext.getResource(url);
172                        return importUrl(resource);
173                } catch (MalformedURLException mux)
174                {
175                        throw new WicketRuntimeException(mux);
176                }
177        }
178
179        /**
180         * Imports from an absolute url.
181         * 
182         * @param url
183         *            the url to import
184         * @return the imported url's contents
185         */
186        private String importAbsoluteUrl(CharSequence url)
187        {
188                try
189                {
190                        return importUrl(new URL(url.toString()));
191                }
192                catch (MalformedURLException e)
193                {
194                        throw new WicketRuntimeException(e);
195                }
196        }
197
198        /**
199         * 
200         * @return The charset of the text to be retrieved and included
201         */
202        public Charset getCharset()
203        {
204                return null;
205        }
206
207        /**
208         * Imports the contents from the given url.
209         * 
210         * @param url
211         *            the url
212         * @return the imported contents
213         */
214        private String importUrl(URL url)
215        {
216                return ResourceUtil.readString(new UrlResourceStream(url), getCharset());
217        }
218}