View Javadoc
1   /**
2    * This file Copyright (c) 2003-2015 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.cms.beans.config;
35  
36  import info.magnolia.context.MgnlContext;
37  import info.magnolia.context.WebContext;
38  
39  import java.util.ArrayList;
40  import java.util.Iterator;
41  import java.util.List;
42  
43  import org.apache.commons.lang3.StringUtils;
44  
45  /**
46   * Simple VirtualURI mapping that can forward to a different url depending on the request host name. The "mappings" node
47   * contains a list of nodes with "host" and "toURI" properties.
48   * See below for a sample configuration:
49   *
50   * <pre>
51   * [] virtualURIMapping
52   *    [] default
53   *     - class            info.magnolia.cms.beans.config.HostBasedVirtualURIMapping
54   *     - fromURI          /
55   *     - toURI            redirect:/.magnolia/pages/adminCentral.html
56   *       [] mappings
57   *        - [] com
58   *          - host      www.acme.com
59   *          - toURI     forward:/acme/en/index.html
60   *        - [] de
61   *          - host      www.acme.de
62   *          - toURI     forward:/acme/de/index.html
63   * </pre>
64   *
65   * For backwards compatibility The "host" property may still be used, it contains
66   * a list of uri=destination strings.
67   * This technique is deprecated primarily because the properties of a node do not have a deterministic order.
68   * See below for a sample configuration of the deprecated approach using 'hosts' node.:
69   *
70   * <pre>
71   * [] virtualURIMapping
72   *    [] default
73   *     - class            info.magnolia.cms.beans.config.HostBasedVirtualURIMapping
74   *     - fromURI          /
75   *     - toURI            redirect:/.magnolia/pages/adminCentral.html
76   *       [] hosts
77   *        - 1             www.acme.com=forward:/acme/en/index.html
78   *        - 2             www.acme.de=forward:/acme/de/index.html
79   * </pre>
80   */
81  
82  public class HostBasedVirtualURIMapping extends DefaultVirtualURIMapping {
83  
84      protected ArrayList<HostURIMapping> hosts;
85  
86      /**
87       *
88       */
89      public HostBasedVirtualURIMapping() {
90          hosts = new ArrayList<HostURIMapping>();
91      }
92  
93      // required by content2bean in order to make addMapping work, do not remove!
94      public List<HostURIMapping> getMappings() {
95          return null;
96      }
97  
98      /**
99       * Adds a mapping (used by content2bean).
100      *
101      * @param mapping object.
102      */
103     public void addMapping(HostURIMapping mapping) {
104         if (mapping.getHost() != null && mapping.getToURI() != null) {
105             synchronized (hosts) {
106                 hosts.add(mapping);
107             }
108         }
109     }
110 
111     // required by content2bean in order to make addHost work, do not remove!
112     // @Deprecated since 4.5.18, content2bean mechanism will recognize getMappings() method to call the addMapping method.
113     @Deprecated
114     public List<String> getHosts() {
115         return null;
116     }
117 
118 
119     /**
120      * @param mapping in the form host=path
121      * @Deprecated since 4.5.18, Use {@link #addMapping(HostURIMapping))} instead.
122      * Adds a host mapping (used by content2bean).
123      */
124     @Deprecated
125     public void addHost(String mapping) {
126         String[] hostToPath = StringUtils.split(mapping, "=");
127         if (hostToPath != null && hostToPath.length == 2) {
128             synchronized (hosts) {
129                 hosts.add(new HostURIMapping(hostToPath[0], hostToPath[1]));
130             }
131         }
132     }
133 
134     @Override
135     public MappingResult mapURI(String uri) {
136 
137         String destination = toURI;
138 
139         if (pattern != null && pattern.match(uri)) {
140             String hostMapping = this.tryToMapHost();
141             if (hostMapping != null) {
142                 destination = hostMapping;
143             }
144 
145             MappingResult r = new MappingResult();
146             r.setLevel(pattern.getLength());
147             r.setToURI(destination);
148             return r;
149         }
150         return null;
151     }
152 
153     /**
154      * Choose the mapping host that matches the request host, with the least number of additional characters.
155      *
156      * @return The URI to map to.
157      */
158     protected String tryToMapHost() {
159         final int NO_MATCHED_MAPPINGS = -1;
160         int bestMatchValue = NO_MATCHED_MAPPINGS;
161         String toURI = null;
162 
163         if (hosts != null && !hosts.isEmpty()) {
164             String requestHost = ((WebContext) MgnlContext.getInstance()).getRequest().getServerName();
165 
166             Iterator<HostURIMapping> hostIt = hosts.iterator();
167 
168             while (hostIt.hasNext()) {
169                 HostURIMapping hk = hostIt.next();
170                 if (requestHost.endsWith(hk.getHost())) {
171                     int matchValue = (requestHost.length() - hk.getHost().length());
172                     if (bestMatchValue == NO_MATCHED_MAPPINGS || matchValue < bestMatchValue) {
173                         bestMatchValue = matchValue;
174                         toURI = hk.getToURI();
175                     }
176                 }
177             }
178         }
179         return toURI;
180     }
181 
182     @Override
183     public String toString() {
184         return "[" + super.toString() + "[hosts:" + hosts.toString() + "]]";
185     }
186 
187     /**
188      * HostURIMapping object to receive "mapping" nodes in configuration.
189      */
190     public static class HostURIMapping {
191 
192         // Empty constructor required for content2bean.
193         public HostURIMapping() {
194         }
195 
196         public HostURIMapping(String host, String toURI) {
197             this.host = host;
198             this.toURI = toURI;
199         }
200 
201         private String host;
202 
203         private String toURI;
204 
205         public String getHost() {
206             return this.host;
207         }
208 
209         public void setHost(String host) {
210             this.host = host;
211         }
212 
213         public String getToURI() {
214             return this.toURI;
215         }
216 
217         public void setToURI(String toURI) {
218             this.toURI = toURI;
219         }
220 
221         @Override
222         public String toString() {
223             return getHost() + "=" + getToURI();
224 
225         }
226     }
227 
228 }