001    /*
002    Copyright (c) 1996-2012, Damon Hart-Davis
003    All rights reserved.
004    
005    Redistribution and use in source and binary forms, with or without
006    modification, are permitted provided that the following conditions are
007    met:
008    
009      * Redistributions of source code must retain the above copyright
010        notice, this list of conditions and the following disclaimer.
011    
012      * Redistributions in binary form must reproduce the above copyright
013        notice, this list of conditions and the following disclaimer in the
014        documentation and/or other materials provided with the
015        distribution.
016    
017    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
018    IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
019    TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
020    PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
021    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
022    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
023    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
024    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
025    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
026    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
027    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028    */
029    
030    package org.hd.d.pg2k.webSvr.virtualHosts;
031    
032    import java.util.Arrays;
033    import java.util.Collections;
034    import java.util.HashMap;
035    import java.util.HashSet;
036    import java.util.Iterator;
037    import java.util.List;
038    import java.util.Map;
039    import java.util.Set;
040    
041    import org.hd.d.pg2k.svrCore.CoreConsts;
042    import org.hd.d.pg2k.svrCore.HostUtils;
043    import org.hd.d.pg2k.webSvr.util.WebConsts;
044    
045    /**
046     * Created by IntelliJ IDEA.
047     * User: Damon Hart-Davis
048     * Date: 27-Jun-2003
049     * Time: 18:08:17
050     */
051    
052    /**Contains details of the virtual hosts supported.
053     * This also contains the current canonical list of skin names.
054     */
055    public final class VirtualHosts
056        {
057        /**Skin name for black background, minimalist page style. */
058        public static final String SKIN_BLACK_MINIMAL = "blackMin";
059    
060        /**Skin name for basic .mobi compliant site. */
061        public static final String SKIN_MOBI_BASIC = "mobiBasic";
062    
063        /**Default virtual host description (ie keyed from main site name) for default livery, etc.
064         * Default livery has normal navigation, advertising, layout, etc.
065         */
066        public static final VirtualHost VIRTUAL_HOST_DEFAULT =
067            new VirtualHost(CoreConsts.MAIN_DATA_HOST, null, null,
068                            new String[]{ // Aliases, some deprecated...
069                                "data.pg2k.hd.org",
070                                // "hd.org", "d.hd.org",
071                            });
072    
073        /**IPv6-only virtual host description (ie keyed from main site name) for default livery, etc.
074         * Default livery has normal navigation, advertising, layout, etc.
075         */
076        public static final VirtualHost VIRTUAL_HOST_IPv6 =
077            new VirtualHost("ipv6.gallery.hd.org", null, null, null);
078    
079        /**IPv6-only virtual host description (ie keyed from main site name) for default livery, etc.
080         * Default livery has normal navigation, advertising, layout, etc.
081         */
082        public static final VirtualHost VIRTUAL_HOST_IPv4 =
083            new VirtualHost("ipv4.gallery.hd.org", null, null, null);
084    
085        /**Virtual host description and main name for SmlPx.
086         * This is a basic .mobi-compliant version of the Gallery.
087         */
088        public static final VirtualHost VIRTUAL_HOST_SMLPX =
089            new VirtualHost("smlpx.mobi",
090                            WebConsts.VIRTUAL_SITE_CUST_SMLPX_ROOT,
091                            SKIN_MOBI_BASIC,
092                            null);
093    
094        /**Virtual host description and main name for Aloha Earth.
095         * Aloha Earth has a black background and minimalist furniture.
096         */
097        public static final VirtualHost VIRTUAL_HOST_ALOHAEARTH =
098            new VirtualHost("aloha-earth.com",
099                            WebConsts.VIRTUAL_SITE_CUST_ALOHAEARTH_ROOT,
100                            SKIN_BLACK_MINIMAL,
101                            new String[]{ // Deprecated aliases...
102                                "aloha-earth.org", "alohaearth.org", "alohaearth.com"
103                            });
104    
105        /**Virtual host description and main name for LondonPix.
106         * LondonPix has a black background and minimalist furniture.
107         */
108        public static final VirtualHost VIRTUAL_HOST_LONDONPIX =
109            new VirtualHost("londonpix.org.uk",
110                            WebConsts.VIRTUAL_SITE_CUST_LONDONPIX_ROOT,
111                            SKIN_BLACK_MINIMAL, null);
112    
113        /**Virtual host description and main name for ZAPix.
114         * ZAPix has a black background and minimalist furniture.
115         */
116        public static final VirtualHost VIRTUAL_HOST_ZAPIX =
117            new VirtualHost("pix.org.za",
118                            WebConsts.VIRTUAL_SITE_CUST_ZAPIX_ROOT,
119                            SKIN_BLACK_MINIMAL,
120                            new String[]{ // Deprecated aliases.
121                                "photo.org.za", "photos.org.za"
122                            });
123    
124        /**Virtual host description and main name for localhost; primarily for testing.
125         * Usually standard livery, but need not be so.
126         */
127        public static final VirtualHost VIRTUAL_HOST_LOCALHOST =
128            new VirtualHost("localhost", null, null, null);
129    
130        /**Virtual host description and main name for localhost as literal IPv4 address; primarily for testing.
131         * Usually standard livery, but need not be so.
132         */
133        public static final VirtualHost VIRTUAL_HOST_127p0p0p1 =
134            new VirtualHost("127.0.0.1", null, null, null);
135    
136        /**Virtual host description and main name for localhost as literal IPv6 address; primarily for testing.
137         * Usually standard livery, but need not be so.
138         */
139        public static final VirtualHost VIRTUAL_HOST_cc1 =
140            new VirtualHost("[::1]", null, null, null);
141    
142        /**Public (immutable) List of canonical "main" VirtualHost descriptions.
143         * In principle all host names or site URIs should map to one of these,
144         * and we may even force redirects and so on so that search engines
145         * and users learn and use these canonical versions.
146         * <p>
147         * This is not intended for fast lookup,
148         * and nothing is implied by the order of entries.
149         * <p>
150         * This should help:
151         * <ul>
152         * <li>Reduce complexity of PG2K management, eg in deployment descriptors.
153         * <li>Reduce redundant Web spider activity to deprecated host names.
154         * <li>Improve load balancing by making better use of "core" aliases.
155         * </ul>
156         */
157        public static final List<VirtualHost> canonicalVirtualHosts =
158            Collections.unmodifiableList(Arrays.asList(new VirtualHost[]{
159                VIRTUAL_HOST_DEFAULT,
160                VIRTUAL_HOST_LONDONPIX,
161                VIRTUAL_HOST_ZAPIX,
162                VIRTUAL_HOST_SMLPX,
163                VIRTUAL_HOST_ALOHAEARTH,
164                VIRTUAL_HOST_IPv4, VIRTUAL_HOST_IPv6,
165                VIRTUAL_HOST_LOCALHOST, VIRTUAL_HOST_127p0p0p1, VIRTUAL_HOST_cc1
166            }));
167    
168        /**Public (immutable) Map from String normalised host name to VirtualHost.
169         * This is built from the main and alias host names.
170         * <p>
171         * For the moment this is hard-wired in;
172         * it may become loadable in GenProps in future.
173         */
174        public static final Map<String,VirtualHost> hostDetails;
175        /**Initialise hostDetails. */
176        static
177            {
178            // Create the underlying host map as a HashMap for speed.
179            final Map<String,VirtualHost> hm = new HashMap<String, VirtualHost>(1 + 3 * canonicalVirtualHosts.size());
180    
181            // Make entries for all the canonical and alias names...
182            for(final Iterator<VirtualHost> it = canonicalVirtualHosts.iterator(); it.hasNext(); )
183                {
184                final VirtualHost vh = it.next();
185                if(hm.put(vh.normalisedName, vh) != null)
186                    { System.err.println("WARNING: duplicate virtual host name: " + vh.normalisedName); }
187                for(final Iterator<String> it2 = vh.normalisedAliasNames.iterator(); it2.hasNext(); )
188                    {
189                    final String an = it2.next();
190                    if(hm.put(an, vh) != null)
191                        { System.err.println("WARNING: duplicate virtual host name: " + an); }
192                    }
193                }
194    
195            hostDetails = Collections.unmodifiableMap(hm);
196            }
197    
198        /**Lookup VirtualHost details by hostname and request URL; null if no match found.
199         * The hostname supplied is normalised before lookup.
200         * <p>
201         * Before a lookup by hostname is attempted,
202         * a match is attempted on the request URI
203         * iff the URI starts with the normal prefix for virtual sites.
204         * If a virtual host root is a prefix of the request URI
205         * then we force usage of the the virtual host livery.
206         *
207         * @param serverHostname  the host name with which the page was fetched,
208         *     as from request.getServerName()
209         * @param requestURI  the within-host URI as from request.getRequestURI(),
210         *     or null for a faster lookup just by normalised name
211         */
212        public static VirtualHost getVirtualHostDetails(final String serverHostname,
213                                                        final String requestURI)
214            {
215            // Try lookup on the URI first iff it looks like a virtual site.
216            if((requestURI != null) &&
217               (requestURI.startsWith(WebConsts.VIRTUAL_SITES_ROOT)))
218                {
219                // This is potentially a little slow,
220                // and does not necessarily return the primary virtual alias.
221                for(final Iterator<VirtualHost> it = hostDetails.values().iterator(); it.hasNext(); )
222                    {
223                    final VirtualHost vh = it.next();
224                    final String dir = vh.virtualRootDir;
225                    if((dir != null) && requestURI.startsWith(dir))
226                        { return(vh); }
227                    }
228                }
229    
230            // Try lookup on the virtual hostname.
231            final VirtualHost result = hostDetails.get(
232                HostUtils.normaliseVirtualHostName(serverHostname));
233            return(result);
234            }
235    
236    
237        /**Details of one virtual host.
238         * This supports hashCode() and equals() which work on the
239         * (normalised) name alone.
240         */
241        public static final class VirtualHost
242            {
243            /**Construct the record of one virtual host.
244             *
245             * @param hostname  non-null name of the host; will be stored normalised
246             * @param root  root URI of home directory starting and ending with '/';
247             *     if null or "/" (stored as null) then normal home dir is to be used
248             * @param skinName  name of livery or skin to be use on each page;
249             *     if null or unrecognised then standard livery may be used
250             * @param aliases  alias host names for the main host specified
251             *     all values supplied will be normalised with duplicates removed;
252             *     may be null or zero-length
253             *     but entries may not be null or zero-length
254             *
255             * @throws IllegalArgumentException  if arguments are malformed
256             */
257            public VirtualHost(final String hostname,
258                               final String root,
259                               final String skinName,
260                               final String[] aliases)
261                {
262                normalisedName = HostUtils.normaliseVirtualHostName(hostname);
263    
264                if((root == null) || root.equals("/"))
265                    { virtualRootDir = null; }
266                else if(!root.startsWith("/") || !root.endsWith("/"))
267                    { throw new IllegalArgumentException(); }
268                else
269                    { virtualRootDir = root; }
270    
271                final Set<String> normAliases = new HashSet<String>();
272                if(aliases != null)
273                    {
274                    for(int i = aliases.length; --i >= 0; )
275                        {
276                        final String a = aliases[i];
277                        if(a == null)
278                            { throw new IllegalArgumentException("null alias not allowed"); }
279                        final String na = HostUtils.normaliseVirtualHostName(a);
280                        if(na.length() == 0)
281                            { throw new IllegalArgumentException("zero-length (normalised) alias not allowed"); }
282    
283                        // Only add this alias if not the same as the main name.
284                        if(!na.equals(normalisedName))
285                            { normAliases.add(na); }
286                        }
287                    }
288                final Set<String> noAliases = Collections.emptySet();
289                normalisedAliasNames = (normAliases.size() == 0) ? noAliases :
290                    Collections.unmodifiableSet(normAliases);
291    
292                skin = skinName;
293                }
294    
295            /**Normalised (prefix-less, all-lower-case, no-final-dot) virtual host name; non-null. */
296            public final String normalisedName;
297    
298            /**Root of virtual host home page in main hierarchy, or null.
299             * Null if normal root (and normal home page) is used,
300             * else starts and ends with '/' but is not "/" and gives position
301             * of opening directory for virtual host.
302             * <p>
303             * The fact that this cannot be "/" makes it safe to do a redirect
304             * without checking the value.
305             */
306            public final String virtualRootDir;
307    
308            /**Skin/livery name to be used; null if default is to be used. */
309            public final String skin;
310    
311            /**Immutable Set of normalised String hostname aliases for this virtual host; never null.
312             * May be empty.
313             * <p>
314             * Entries are assumed to be deprecated and visitors (and spiders)
315             * arriving at these may be diverted to the normalisedName
316             * to concentrate activity on it.
317             */
318            public final Set<String> normalisedAliasNames;
319    
320            /**Return the normalisedName for the String representation. */
321            @Override
322            public String toString() { return(normalisedName); }
323            }
324        }