Wiki source code of Short XWiki URLs
Version 34.2 by Vincent Massol on 2013/09/19
Hide last authors
author | version | line-number | content |
---|---|---|---|
![]() |
19.4 | 1 | {{box cssClass="floatinginfobox" title="**Contents**"}} |
2 | {{toc/}} | ||
3 | {{/box}} | ||
![]() |
14.1 | 4 | |
![]() |
19.4 | 5 | This tutorial shows you how to tune your XWiki platform by replacing the default URL scheme with a shorter scheme. |
![]() |
15.1 | 6 | |
![]() |
14.1 | 7 | {{info}} |
8 | A short URL is an URL without the ##xwiki/bin/view## parts. | ||
9 | {{/info}} | ||
![]() |
2.1 | 10 | |
![]() |
20.1 | 11 | = I. Application name = |
![]() |
1.1 | 12 | |
![]() |
14.1 | 13 | The ##/xwiki/## part of the URL is the application name. It identifies the application that should process the request, and it allows a container to host more than one application. To change it you must refer to your container's documentation and find how to map the context path of a web application. For example on Tomcat it's enough to simply deploy the XWiki webapp in the ##webapps## directory, in a sub directory named after the application name you wish to use (e.g. ##webapps/myappname##). |
![]() |
1.1 | 14 | |
![]() |
26.1 | 15 | A special case is when deploying XWiki as the ROOT application, which actually allows the application name part to be empty, so an URL can take the form ##server.com/bin/view/Space/Document##. Achieving this depends on the container, as there's no standard regarding the ROOT application. For example: |
16 | * in Tomcat, with the default configuration, all it takes is to deploy the XWiki web application in ##webapps##, in a sub directory named ##ROOT## (i.e. ##webapps/ROOT##). | ||
17 | * In Jetty, with the default configuration, all it takes is to deploy the XWiki web application in ##webapps##, in a sub directory named ##root##. Note that if you're using the Standalone distribution (which packages Jetty and HSQLDB) then you'll also need to: | ||
18 | ** Remove the existing ##webapps/root## directory which contains a redirect Servlet that automatically redirects root URLs to the ##xwiki## context. You won't need that anymore. | ||
19 | ** Rename the existing ##webapps/xwiki## directory into ##webapps/root##. | ||
20 | ** Remove the ##jetty/contexts/xwiki.xml## file and thus keep only the ##jetty/contexts/root.xml## file. Otherwise you'll get a warning in the console. | ||
![]() |
10.2 | 21 | |
![]() |
26.1 | 22 | Refer to your container's documentation for more details. |
23 | |||
![]() |
20.1 | 24 | = II. Servlet mapping name = |
![]() |
1.1 | 25 | |
![]() |
29.1 | 26 | The second part is the hardest part to remove. It identifies the servlet that should process the page, which, for ##/bin/##, is the Struts servlet. Generically speaking, to get rid of ##/bin/##, you need to configure your system so that URLs matching ##/*## are mapped to the Struts Servlet (by default only ##/bin/*## URLs are mapped to the Struts Servlet). |
![]() |
1.1 | 27 | |
![]() |
29.1 | 28 | However you need to be careful that the following prefixes do NOT go through the Struts Servlet (see your ##web.xml## to check their mappings): |
29 | * ##/resources/*## and ##/skins/*##: Statically served resources. These need to be served directly as static resources. | ||
30 | * ##/rest/*##: REST resources | ||
31 | * ##/xmlrpc/*##: XML-RPC resources | ||
32 | * ##*.gwtrpc##: GWT-RPC calls | ||
33 | * ##/webdav/*##: WebDav calls | ||
34 | * ##/XWikiService##: XWiki GWT Servlet | ||
35 | * ##/redirect##: The XWiki Redirect Servlet used to redirect to the home page when no page is specified in the URL | ||
![]() |
1.1 | 36 | |
![]() |
29.1 | 37 | There are various alternate ways to achieve this as detailed below. |
![]() |
20.1 | 38 | |
![]() |
34.2 | 39 | Now XWiki also generates URL and you can tell it to generate URLs without the ##bin/## part by adding this piece of code in ##xwiki.cfg##: {{code language="none"}}xwiki.defaultservletpath={{/code}} (this is not required with the UrlRewriteFilter solution since it rewrites outbound URLs too). |
![]() |
29.1 | 40 | |
41 | == UrlRewriteFilter == | ||
42 | |||
43 | This is a [[framework offering a Servlet Filter>>http://www.tuckey.org/urlrewrite/]] allowing to rewrite URLs. Install it and drop the following configuration in ##WEB-INF/urlrewrite.xml##: | ||
44 | |||
![]() |
14.1 | 45 | {{code language="xml"}} |
![]() |
29.1 | 46 | <?xml version="1.0" encoding="utf-8"?> |
47 | <!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 4.0//EN" | ||
48 | "http://www.tuckey.org/res/dtds/urlrewrite4.0.dtd"> | ||
49 | <urlrewrite> | ||
50 | |||
51 | <rule> | ||
52 | <note> | ||
53 | Ensure that URLs ending with .gwtrpc are not modified. | ||
54 | </note> | ||
55 | <from>^/(.*)\.gwtrpc$</from> | ||
56 | <to type="forward" last="true">-</to> | ||
57 | </rule> | ||
58 | |||
59 | <rule> | ||
60 | <note> | ||
61 | En sure that URLs that must not be served by the Struts Servlet are not modified. | ||
62 | </note> | ||
63 | <from>^/(bin|resources|skins|rest|webdav|xmlrpc)/(.*)$</from> | ||
64 | <to type="forward" last="true">-</to> | ||
65 | </rule> | ||
66 | |||
67 | <rule> | ||
68 | <note> | ||
69 | For all other URLs we prepend the "/bin/" prefix so that they get routed to the XWiki Action Servlet. | ||
70 | </note> | ||
71 | <from>^/(.*)$</from> | ||
72 | <to type="forward">/bin/$1</to> | ||
73 | </rule> | ||
74 | |||
![]() |
32.1 | 75 | <outbound-rule> |
76 | <note> | ||
77 | Rewrite outbound URLs to remove the "/bin" part. | ||
78 | </note> | ||
79 | <from>/bin/(.*)/(.*)$</from> | ||
80 | <to>/$1/$2</to> | ||
81 | </outbound-rule> | ||
82 | |||
![]() |
29.1 | 83 | </urlrewrite> |
84 | {{/code}} | ||
85 | |||
![]() |
33.1 | 86 | {{warning}} |
![]() |
32.1 | 87 | The outbound URL rewriting [[works only with XWiki 5.2+>>http://jira.xwiki.org/browse/XWIKI-9430]]. |
![]() |
30.1 | 88 | {{/warning}} |
89 | |||
![]() |
29.1 | 90 | == Configuring web.xml == |
91 | |||
92 | You could be tempted to configure the XWiki's ##web.xml## file as follows below but **note that the WYSIWYG editor won't work with this solution**. The problem is that the ##/*## mapping will override the ##/*.gwtrpc## mapping. This is caused by the Servlet spec which says: | ||
93 | |||
94 | > The URL path mapping rules below are used in order. The first successful match is used with no further matches attempted: | ||
95 | > 1. The container will try to find an exact match of the path of the request to the path of the servlet. A successful match selects the servlet. | ||
96 | > 2. The container will recursively try to match the longest path-prefix. This is done by stepping down the path tree a directory at a time, using the '/' character as a path separator. The longest match determines the servlet selected. | ||
97 | > 3. If the last segment in the URL path contains an extension (e.g. .jsp), the servlet container will try to match a servlet that handles requests for the extension. An extension is defined as the part of the last segment after the last '.' character. | ||
98 | > 4. If neither of the previous three rules result in a servlet match, the container will attempt to serve content appropriate for the resource requested. If a "default" servlet is defined for the application, it will be used. | ||
99 | |||
100 | In any case if you don't use the WYSIWYG editor this could be a good solution so here it is just in case: | ||
101 | * Copy the mapping for the Struts servlet to also be activated for ##/*##:((( | ||
102 | {{code language="xml"}} | ||
103 | <servlet-mapping> | ||
104 | <servlet-name>action</servlet-name> | ||
105 | <url-pattern>/*</url-pattern> | ||
106 | </servlet-mapping> | ||
107 | {{/code}} | ||
108 | |||
109 | Be sure to leave the other mappings in place, so that ##/bin/## works, too. | ||
110 | ))) | ||
111 | * Configure your container to statically serve ##/resources/*## and ##/skins/*## resources. By default we don't have any mapping for those in ##web.xml## which means they are statically served (by the Default Servlet). Configuring the Default Servlet is container-dependent unfortunately. | ||
112 | ** In Jetty, the container shipped with the XWiki installer, you will have to write: | ||
113 | *** For Jetty <= 6.x:((( | ||
114 | {{code language="xml"}} | ||
![]() |
27.1 | 115 | <servlet> |
![]() |
4.1 | 116 | <servlet-name>defaultSkins</servlet-name> |
![]() |
12.1 | 117 | <servlet-class>org.mortbay.jetty.servlet.DefaultServlet</servlet-class> |
![]() |
4.1 | 118 | <load-on-startup>1</load-on-startup> |
119 | </servlet> | ||
120 | <servlet-mapping> | ||
121 | <servlet-name>defaultSkins</servlet-name> | ||
122 | <url-pattern>/skins/*</url-pattern> | ||
123 | </servlet-mapping> | ||
![]() |
18.1 | 124 | <servlet-mapping> |
125 | <servlet-name>defaultSkins</servlet-name> | ||
126 | <url-pattern>/resources/*</url-pattern> | ||
127 | </servlet-mapping> | ||
![]() |
14.1 | 128 | {{/code}} |
![]() |
27.1 | 129 | ))) |
![]() |
29.1 | 130 | *** For Jetty >= 7.x:((( |
![]() |
27.2 | 131 | {{code language="xml"}} |
![]() |
27.1 | 132 | <servlet> |
133 | <servlet-name>defaultSkins</servlet-name> | ||
134 | <servlet-class>org.eclipse.jetty.servlet.DefaultServlet</servlet-class> | ||
135 | <load-on-startup>1</load-on-startup> | ||
136 | </servlet> | ||
137 | <servlet-mapping> | ||
138 | <servlet-name>defaultSkins</servlet-name> | ||
139 | <url-pattern>/skins/*</url-pattern> | ||
140 | </servlet-mapping> | ||
141 | <servlet-mapping> | ||
142 | <servlet-name>defaultSkins</servlet-name> | ||
143 | <url-pattern>/resources/*</url-pattern> | ||
144 | </servlet-mapping> | ||
![]() |
27.2 | 145 | {{/code}} |
![]() |
27.1 | 146 | ))) |
![]() |
29.1 | 147 | ** In Tomcat, the default Servlet does not accept a parameter for changing the resource base, so you would need to write another default servlet. |
![]() |
4.1 | 148 | |
![]() |
29.1 | 149 | == web.xml + Apache == |
![]() |
16.1 | 150 | |
![]() |
29.2 | 151 | Do as above but instead of mapping the Default Servlet, bypass the Servlet container at the web front-end level. For example, if you are using Apache httpd as a front-end, and assuming a webapp deployed as a ROOT webapp and an AJP connection between httpd and the servlet container, the following configuration allows you to serve skin files and static resources directly from httpd: |
![]() |
4.1 | 152 | |
![]() |
19.1 | 153 | {{code}} |
![]() |
19.4 | 154 | Alias /skins /usr/local/xwiki/skins |
155 | Alias /resources /usr/local/xwiki/resources | ||
156 | ProxyPass /skins/ ! | ||
157 | ProxyPass /resources/ ! | ||
![]() |
19.1 | 158 | {{/code}} |
159 | |||
![]() |
34.1 | 160 | Note that this solution will not work for the WYSIWYG editor since it needs that the ##.gwtrpc## URLs are directed to the XWiki Servlet. The following Apache configuration using URL rewriting achieves this: |
161 | |||
162 | {{code}} | ||
163 | Alias /skins /usr/local/xwiki/skins | ||
164 | Alias /resources /usr/local/xwiki/resources | ||
165 | |||
166 | RewriteEngine on | ||
167 | |||
168 | RewriteRule ^/+skins - [L] | ||
169 | RewriteCond %{REQUEST_URI} !\.gwtrpc$ | ||
170 | RewriteRule ^/+resources($|/.*) - [L] | ||
171 | |||
172 | RewriteRule .* http://localhost:8080%{REQUEST_URI} [P,L] | ||
173 | ProxyPassReverse / http://localhost:8080/ | ||
174 | {{/code}} | ||
175 | |||
176 | {{warning}}Note that while this has been tested to work with XWiki 3.1, it seems it [[doesn't work with 4.5.4 and later>>http://jira.xwiki.org/browse/XWIKI-9455]].{{/warning}} | ||
177 | |||
![]() |
29.1 | 178 | == Lighttpd + Jetty == |
![]() |
20.1 | 179 | |
![]() |
19.4 | 180 | I used lighttpd, but I assume it can be done with other webservers too. This is the configuration I used in the lighttpd config (note that my xwiki folder has been moved to ##/usr/share/jetty/webapps/root## (no 'xwiki' at all)): |
![]() |
12.1 | 181 | |
![]() |
14.1 | 182 | {{code language="none"}} |
![]() |
12.1 | 183 | $HTTP["host"] =~ "^www\.domain\.com$" { |
184 | # ensure all requests for .gwtrpc files go through to java server | ||
185 | # we can put this rule first as a higher priority, which java couldn't do | ||
186 | $HTTP["url"] =~ "\.gwtrpc$" { | ||
187 | proxy.server = ( "" => (( "host" => "127.0.0.1", "port" => 8080 ))) | ||
188 | } | ||
189 | # otherwise, we can handle the static resources | ||
190 | else $HTTP["url"] =~ "^/resources/" { | ||
191 | alias.url += ( "/resources" => "/usr/share/jetty/webapps/root/resources" ) | ||
192 | } | ||
193 | # otherwise, we can handle the static resources | ||
194 | else $HTTP["url"] =~ "^/skins/" { | ||
195 | alias.url += ( "/skins" => "/usr/share/jetty/webapps/root/skins" ) | ||
196 | } | ||
197 | # and here is the primary server | ||
198 | else $HTTP["host"] =~ "^www\.domain\.com$" { | ||
199 | proxy.server = ( "" => (( "host" => "127.0.0.1", "port" => 8080 ))) | ||
200 | } | ||
201 | } | ||
202 | # redirect anything.domain.com to www.domain.com | ||
203 | else $HTTP["host"] =~ "\.domain\.com$" { | ||
204 | url.redirect = ( "^/(.*)" => "http://www.domain.com/$1" ) | ||
205 | server.name = "www.domain.com" | ||
206 | } | ||
207 | # redirect domain.com to www.domain.com | ||
208 | else $HTTP["host"] =~ "domain\.com$" { | ||
209 | url.redirect = ( "^/(.*)" => "http://www.domain.com/$1" ) | ||
210 | server.name = "www.domain.com" | ||
211 | } | ||
![]() |
14.1 | 212 | {{/code}} |
![]() |
12.1 | 213 | |
![]() |
29.1 | 214 | So lighttpd will serve any static content unless it has ##.gwtrpc## on the end of the URL. |
![]() |
12.1 | 215 | |
![]() |
29.1 | 216 | If you use Nginx as a web-server, just add three more locations and set ##root## to them. By ##try_files## Nginx checks static content presence and if doesn't exist, redirect it to the Tomcat (we expect dynamic content in this case, including all ##*.gwtrpc## requests). |
![]() |
23.1 | 217 | |
218 | {{code}} | ||
219 | location /skins/ { | ||
220 | root /var/lib/tomcat7/webapps/ROOT; | ||
221 | } | ||
222 | |||
223 | location /resources/ { | ||
224 | try_files $uri $uri/ @fallback; | ||
225 | root /var/lib/tomcat7/webapps/ROOT; | ||
226 | } | ||
227 | |||
228 | location @fallback { | ||
229 | proxy_pass http://localhost:8080; | ||
230 | } | ||
231 | {{/code}} | ||
232 | |||
![]() |
25.1 | 233 | In the example above XWiki installed as ROOT application in Tomcat. Change path to your XWiki application accordingly. |
![]() |
24.1 | 234 | |
![]() |
29.1 | 235 | Then in ##web.xml##, I changed the ##gwtrpc## mapping to: |
![]() |
24.1 | 236 | |
![]() |
14.1 | 237 | {{code language="xml"}} |
238 | <servlet-mapping> | ||
![]() |
12.1 | 239 | <servlet-name>gwtrpc</servlet-name> |
240 | <url-pattern>/resources/*</url-pattern> | ||
241 | <url-pattern>/skins/*</url-pattern> | ||
242 | </servlet-mapping> | ||
![]() |
14.1 | 243 | {{/code}} |
![]() |
12.1 | 244 | |
![]() |
29.1 | 245 | Since we are using a url-pattern of ##/path/##, it will be specific enough to be a higher priority than the ##/## pattern we'll use next. And since the only thing that will come through via resources or skins will be ##gwtrpc##, then we can be sure it's ok. Note that only ##resources## is required, but I did both anyway. |
![]() |
12.1 | 246 | |
![]() |
29.1 | 247 | Now, as described above, add a rule to catch everything else and redirect it to your XWiki servlet: |
![]() |
14.1 | 248 | |
249 | {{code language="xml"}} | ||
250 | <servlet-mapping> | ||
![]() |
12.1 | 251 | <servlet-name>action</servlet-name> |
252 | <url-pattern>/*</url-pattern> | ||
253 | </servlet-mapping> | ||
![]() |
14.1 | 254 | {{/code}} |
![]() |
12.1 | 255 | |
![]() |
20.1 | 256 | = III. Struts action name = |
![]() |
14.1 | 257 | |
![]() |
19.4 | 258 | The third part, ##/view/##, identifies the struts action that should process a request. So this tells what we want to do with the document, ##/view/## it, ##/edit/## it or ##/delete/## it, for example. The XWiki platform allows this part to be missing, considering that the default action is to just display the document, so an URL like ##server.com/bin/Space/Document## will work out of the box. |
259 | |||
260 | Even more, the URL factory, the component that generates URLs, can be configured to skip this part when the action is ##/view/##. To do this write this code in ##xwiki.cfg##: {{code language="none"}}xwiki.showviewaction=0{{/code}}. | ||
261 | |||
![]() |
20.1 | 262 | = IV. Error Page = |
![]() |
19.4 | 263 | |
![]() |
14.1 | 264 | At the ##WEB-INF/web.xml##, the ##location## of the 404 error code needs to be changed accordingly. For example: |
265 | |||
266 | {{code language="xml"}} | ||
267 | <error-page> | ||
![]() |
9.1 | 268 | <error-code>404</error-code> |
269 | <!--<location>/xwiki/bin/view/Main/DocumentDoesNotExist</location>--> | ||
270 | <location>/bin/Main/DocumentDoesNotExist</location> | ||
271 | </error-page> | ||
![]() |
14.1 | 272 | {{/code}} |
![]() |
9.1 | 273 | |
![]() |
20.1 | 274 | = V. Conclusion = |
![]() |
4.1 | 275 | |
276 | After performing all these changes, you should be able to access documents with URLs like: | ||
![]() |
14.1 | 277 | |
![]() |
4.1 | 278 | * server.com/Space/Document |
279 | * server.com/Space/ (pointing to Space.WebHome) | ||
280 | * server.com/Document (pointing to Main.Document) | ||
281 | * server.com/ will show Main.WebHome, without any redirect. | ||
282 | |||
![]() |
5.1 | 283 | As a bonus, these changes are backwards compatible, meaning that any currently working URL will also work with these changes performed, so you won't have any broken bookmarks. |