1
2
3
4 package org.rcfaces.core.internal.contentProxy;
5
6 import java.io.IOException;
7 import java.io.InputStream;
8 import java.net.InetAddress;
9 import java.net.UnknownHostException;
10 import java.util.ArrayList;
11 import java.util.Collections;
12 import java.util.HashMap;
13 import java.util.List;
14 import java.util.Map;
15 import java.util.StringTokenizer;
16 import java.util.regex.Matcher;
17 import java.util.regex.Pattern;
18
19 import javax.faces.FacesException;
20 import javax.faces.context.ExternalContext;
21 import javax.faces.context.FacesContext;
22 import javax.servlet.ServletRequest;
23
24 import org.apache.commons.digester.Digester;
25 import org.apache.commons.digester.Rule;
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28 import org.rcfaces.core.internal.Constants;
29 import org.rcfaces.core.internal.RcfacesContext;
30 import org.rcfaces.core.internal.contentAccessor.BasicContentAccessor;
31 import org.rcfaces.core.internal.contentAccessor.IContentAccessor;
32 import org.rcfaces.core.internal.contentAccessor.IContentPath;
33 import org.rcfaces.core.internal.contentAccessor.IContentProxyHandler;
34 import org.rcfaces.core.internal.contentAccessor.IGeneratedResourceInformation;
35 import org.rcfaces.core.internal.lang.StringAppender;
36 import org.rcfaces.core.internal.util.PathTypeTools;
37 import org.rcfaces.core.provider.AbstractProvider;
38
39
40
41
42
43
44 public class ResourceProxyHandlerImpl extends AbstractProvider implements
45 IContentProxyHandler, IResourceProxyHandler {
46
47 private static final String REVISION = "$Revision: 1.2 $";
48
49 private static final Log LOG = LogFactory
50 .getLog(ResourceProxyHandlerImpl.class);
51
52 private static final String RESOURCES_DEFAULT_PROXY_URL_PARAMETER = Constants
53 .getPackagePrefix()
54 + ".RESOURCE_PROXY_DEFAULT_URL";
55
56 private static final String RESOURCES_PROXY_RULES_PATHS_PARAMETER = Constants
57 .getPackagePrefix()
58 + ".RESOURCE_PROXY_RULES_PATHS";
59
60 private static final String ENABLE_FILTRED_RESOURCES_PROXY_PARAMETER = Constants
61 .getPackagePrefix()
62 + ".ENABLE_FILTRED_RESOURCES_PROXY";
63
64 private static final String ENABLE_FRAMEWORK_RESOURCES_PROXY_PARAMETER = Constants
65 .getPackagePrefix()
66 + ".ENABLE_FRAMEWORK_RESOURCES_PROXY";
67
68 protected static final String PROTOCOL_KEYWORD = "protocol";
69
70 protected static final String LOCAL_NAME_KEYWORD = "host";
71
72 protected static final String LOCAL_ADDR_KEYWORD = "addr";
73
74 protected static final String LOCAL_PORT_KEYWORD = "port";
75
76 protected static final String OPTIONAL_LOCAL_PORT_KEYWORD = "optional-port";
77
78 protected static final String CONTEXT_PATH_KEYWORD = "context";
79
80 protected static final int DEFAULT_HTTP_PORT = 80;
81
82 protected static final int DEFAULT_HTTPS_PORT = 443;
83
84 private static final String CACHED_URL_BASE_PROPERTY = "org.rcfaces.core.CACHED_URL_BASE";
85
86 private static final String NO_BASE_URL_STRING = CACHED_URL_BASE_PROPERTY;
87
88 private static final String DISABLED_URL = "disabled";
89
90 private IPatternRule defaultRule;
91
92 private IPatternRule rules[];
93
94 private boolean enabled;
95
96 private final Map keywordValueMap = new HashMap(16);
97
98 private boolean filtredResourcesEnabled;
99
100 private boolean frameworkResourceEnabled;
101
102 public String getId() {
103 return ID;
104 }
105
106 public void startup(FacesContext facesContext) {
107
108 ExternalContext externalContext = facesContext.getExternalContext();
109
110 final String defaultURL = externalContext
111 .getInitParameter(RESOURCES_DEFAULT_PROXY_URL_PARAMETER);
112
113 String rulesPaths = externalContext
114 .getInitParameter(RESOURCES_PROXY_RULES_PATHS_PARAMETER);
115
116 if ((defaultURL == null || "none".equalsIgnoreCase(defaultURL))
117 && (rulesPaths == null || rulesPaths.trim().length() == 0)) {
118 LOG
119 .info("Disable application proxy rewriting engine. (context parameter '"
120 + RESOURCES_DEFAULT_PROXY_URL_PARAMETER
121 + "="
122 + defaultURL + "')");
123 return;
124 }
125
126 enabled = true;
127
128 if (defaultURL != null) {
129 this.defaultRule = new DefaultPatternRule(defaultURL, null);
130 }
131
132 if (rulesPaths != null && rulesPaths.length() > 0) {
133 loadRules(externalContext, rulesPaths);
134 }
135
136 filtredResourcesEnabled = "true".equalsIgnoreCase(externalContext
137 .getInitParameter(ENABLE_FILTRED_RESOURCES_PROXY_PARAMETER));
138
139 frameworkResourceEnabled = ("false".equalsIgnoreCase(externalContext
140 .getInitParameter(ENABLE_FRAMEWORK_RESOURCES_PROXY_PARAMETER)) == false);
141
142 LOG.info("Set proxy pattern to '" + defaultURL
143 + "', filtredResourcesEnabled=" + filtredResourcesEnabled
144 + ", frameworkResourceEnabled=" + frameworkResourceEnabled
145 + ".");
146
147 RcfacesContext rcfacesContext = RcfacesContext
148 .getInstance(facesContext);
149
150 if (rcfacesContext.getResourceProxyHandler() == null) {
151 rcfacesContext.setResourceProxyHandler(this);
152
153 if (rcfacesContext.getDefaultContentProxyHandler() == null) {
154 rcfacesContext.setDefaultContentProxyHandler(this);
155 }
156 }
157
158 fillKeywordValues(rcfacesContext, keywordValueMap);
159
160 }
161
162 public boolean isEnabled() {
163 return enabled;
164 }
165
166 public boolean isFiltredResourcesEnabled() {
167 return filtredResourcesEnabled;
168 }
169
170 public boolean isFrameworkResourcesEnabled() {
171 return frameworkResourceEnabled;
172 }
173
174 public IContentAccessor getProxyedContentAccessor(
175 RcfacesContext rcfacesContext, FacesContext facesContext,
176 IContentAccessor contentAccessor,
177 IGeneratedResourceInformation[] contentInformationRef) {
178
179 int pathType = contentAccessor.getPathType();
180 if (pathType != IContentPath.RELATIVE_PATH_TYPE
181 && pathType != IContentPath.ABSOLUTE_PATH_TYPE
182 && pathType != IContentPath.CONTEXT_PATH_TYPE) {
183
184 return contentAccessor;
185 }
186
187 if (facesContext == null) {
188 facesContext = FacesContext.getCurrentInstance();
189 }
190
191 String url = (String) contentAccessor.getContentRef();
192
193 if (pathType == IContentPath.RELATIVE_PATH_TYPE) {
194
195
196 url = PathTypeTools.convertRelativePathToContextPath(facesContext,
197 url, null);
198
199 } else if (pathType == IContentPath.ABSOLUTE_PATH_TYPE) {
200
201
202 String converted = PathTypeTools.convertAbsolutePathToContextType(
203 facesContext, url);
204
205 if (converted == null) {
206 throw new FacesException("Can not convert '" + url
207 + "' to context path type.");
208 }
209
210 url = converted;
211 }
212
213 String converted = computeProxyedURL(facesContext, contentAccessor,
214 contentInformationRef, url);
215
216 if (LOG.isDebugEnabled()) {
217 LOG.debug("computeNewURL returns '" + converted + "'.");
218 }
219
220 if (converted == null) {
221 return contentAccessor;
222 }
223
224 return new BasicContentAccessor(facesContext, converted,
225 contentAccessor, IContentPath.EXTERNAL_PATH_TYPE);
226 }
227
228 public String computeProxyedURL(FacesContext facesContext,
229 IContentAccessor contentAccessor,
230 IGeneratedResourceInformation[] contentInformationRef, String url) {
231
232 if (rules != null) {
233 for (int i = 0; i < rules.length; i++) {
234 IPatternRule rule = rules[i];
235
236 if (rule.acceptURL(url) == false) {
237 continue;
238 }
239
240 String ret = computeProxyedURL(facesContext, contentAccessor,
241 contentInformationRef, rule, url);
242 if (ret != null) {
243 if (ret == DISABLED_URL) {
244 return null;
245 }
246 return ret;
247 }
248 }
249 }
250
251 if (defaultRule != null) {
252 String ret = computeProxyedURL(facesContext, contentAccessor,
253 contentInformationRef, defaultRule, url);
254 if (ret != null) {
255 if (ret == DISABLED_URL) {
256 return null;
257 }
258 return ret;
259 }
260 }
261
262 return null;
263 }
264
265 protected String computeProxyedURL(FacesContext facesContext,
266 IContentAccessor contentAccessor,
267 IGeneratedResourceInformation[] contentInformationRef,
268 IPatternRule rule, String url) {
269
270 Map requestMap = facesContext.getExternalContext().getRequestMap();
271
272 String baseURL = (String) requestMap.get(rule);
273
274 if (baseURL == NO_BASE_URL_STRING) {
275 return null;
276 }
277 if (baseURL == DISABLED_URL) {
278 return DISABLED_URL;
279 }
280
281 if (baseURL == null) {
282 baseURL = computeRequestURLBase(facesContext, contentAccessor,
283 contentInformationRef, rule);
284 if (baseURL == null) {
285 requestMap.put(rule, NO_BASE_URL_STRING);
286
287 return null;
288 }
289 if (baseURL == DISABLED_URL) {
290 requestMap.put(rule, DISABLED_URL);
291
292 return baseURL;
293 }
294 if (baseURL.endsWith("/")) {
295 baseURL = baseURL.substring(0, baseURL.length() - 1);
296 }
297
298 requestMap.put(rule, baseURL);
299 }
300
301 StringAppender sa = new StringAppender(baseURL, url.length() + 2);
302
303 if (url.startsWith("/") == false) {
304 sa.append('/');
305 }
306
307 sa.append(url);
308
309 String converted = sa.toString();
310
311 return converted;
312 }
313
314 protected String computeRequestURLBase(FacesContext facesContext,
315 IContentAccessor contentAccessor,
316 IGeneratedResourceInformation[] contentInformationRef,
317 IPatternRule rule) {
318
319 String url = rule.getBaseURI();
320
321 if (DISABLED_URL.equals(url)) {
322 return DISABLED_URL;
323 }
324
325 if (url.indexOf('[') < 0) {
326 if (LOG.isDebugEnabled()) {
327 LOG.debug("Set URL base to pattern '" + url
328 + "'. (no replacement)");
329 }
330
331 return url;
332 }
333
334 char chs[] = url.toCharArray();
335
336 StringAppender sa = new StringAppender(url.length() * 2);
337 int first = 0;
338
339 for (int i = 0; i < chs.length; i++) {
340 char c = chs[i];
341
342 if (c != '[') {
343 continue;
344 }
345
346 if (first < i) {
347 sa.append(chs, first, i - first);
348
349 first = i;
350 }
351
352 int j = i + 1;
353 for (i = j; i < chs.length; i++) {
354 c = chs[i];
355 if (c == ']') {
356 break;
357 }
358 }
359
360 if (i == chs.length) {
361
362
363 throw new FacesException("Invalid pattern '" + url + "'.");
364 }
365
366 String keyword = url.substring(j, i);
367
368 IKeywordValue keywordValue = (IKeywordValue) keywordValueMap
369 .get(keyword);
370 if (keywordValue != null) {
371 String ret = keywordValue.getValue(facesContext, keyword);
372 if (ret != null) {
373 sa.append(ret);
374 }
375 }
376
377 first = i + 1;
378 }
379
380 if (first == 0) {
381 if (LOG.isDebugEnabled()) {
382 LOG.debug("Set URL base to pattern '" + url + "'.");
383 }
384
385 } else if (first < chs.length) {
386 sa.append(chs, first, chs.length - first);
387 }
388
389 String prefix = sa.toString();
390
391 if (LOG.isDebugEnabled()) {
392 LOG.debug("Set URL base to '" + prefix + "'.");
393 }
394
395 return prefix;
396 }
397
398 protected void fillKeywordValues(RcfacesContext rcfacesContext,
399 Map replaceMap) {
400
401 replaceMap.put(PROTOCOL_KEYWORD, new IKeywordValue() {
402 private static final String REVISION = "$Revision: 1.2 $";
403
404 public String getValue(FacesContext facesContext, String keyword) {
405 Object request = facesContext.getExternalContext().getRequest();
406 if ((request instanceof ServletRequest) == false) {
407 return null;
408 }
409
410 ServletRequest servletRequest = (ServletRequest) request;
411
412 String protocol = servletRequest.getScheme();
413 if (protocol != null && protocol.length() > 0) {
414 return protocol;
415 }
416
417 return null;
418 }
419 });
420
421 replaceMap.put(LOCAL_NAME_KEYWORD, new IKeywordValue() {
422 private static final String REVISION = "$Revision: 1.2 $";
423
424 public String getValue(FacesContext facesContext, String keyword) {
425 Object request = facesContext.getExternalContext().getRequest();
426 if ((request instanceof ServletRequest) == false) {
427 return null;
428 }
429
430 ServletRequest servletRequest = (ServletRequest) request;
431
432 String localName = servletRequest.getServerName();
433 if (localName != null && localName.length() > 0) {
434 return localName;
435 }
436
437 return null;
438 }
439 });
440
441 replaceMap.put(LOCAL_ADDR_KEYWORD, new IKeywordValue() {
442 private static final String REVISION = "$Revision: 1.2 $";
443
444 public String getValue(FacesContext facesContext, String keyword) {
445 Object request = facesContext.getExternalContext().getRequest();
446 if ((request instanceof ServletRequest) == false) {
447 return null;
448 }
449
450 ServletRequest servletRequest = (ServletRequest) request;
451
452 String localAddr = servletRequest.getServerName();
453 if (localAddr != null && localAddr.length() > 0) {
454 InetAddress inetAddress;
455 try {
456 inetAddress = InetAddress.getByName(localAddr);
457
458 } catch (UnknownHostException e) {
459 LOG.error("Can not getByName '" + localAddr + "'.", e);
460 inetAddress = null;
461 }
462
463 if (inetAddress != null) {
464 String address = inetAddress.getHostAddress();
465 if (address != null) {
466 return address;
467 }
468 }
469 }
470
471 return null;
472 }
473 });
474
475 replaceMap.put(LOCAL_PORT_KEYWORD, new IKeywordValue() {
476 private static final String REVISION = "$Revision: 1.2 $";
477
478 public String getValue(FacesContext facesContext, String keyword) {
479 Object request = facesContext.getExternalContext().getRequest();
480 if ((request instanceof ServletRequest) == false) {
481 return null;
482 }
483
484 ServletRequest servletRequest = (ServletRequest) request;
485
486 int port = servletRequest.getServerPort();
487 if (port > 0) {
488 return String.valueOf(port);
489 }
490
491 return null;
492 }
493 });
494
495 replaceMap.put(OPTIONAL_LOCAL_PORT_KEYWORD, new IKeywordValue() {
496 private static final String REVISION = "$Revision: 1.2 $";
497
498 public String getValue(FacesContext facesContext, String keyword) {
499 Object request = facesContext.getExternalContext().getRequest();
500 if ((request instanceof ServletRequest) == false) {
501 return null;
502 }
503
504 ServletRequest servletRequest = (ServletRequest) request;
505
506 String protocol = servletRequest.getScheme();
507 int port = servletRequest.getServerPort();
508
509 if ("http".equalsIgnoreCase(protocol)
510 && port == DEFAULT_HTTP_PORT) {
511
512 return null;
513
514 } else if ("https".equalsIgnoreCase(protocol)
515 && port == DEFAULT_HTTPS_PORT) {
516 return null;
517 }
518
519 if (port > 0) {
520 return ":" + port;
521 }
522
523 return null;
524 }
525 });
526
527 replaceMap.put(CONTEXT_PATH_KEYWORD, new IKeywordValue() {
528 private static final String REVISION = "$Revision: 1.2 $";
529
530 public String getValue(FacesContext facesContext, String keyword) {
531 String requestContextPath = facesContext.getExternalContext()
532 .getRequestContextPath();
533 if (requestContextPath != null) {
534 if (requestContextPath.length() > 0
535 && requestContextPath.charAt(0) == '/') {
536 return requestContextPath.substring(1);
537 }
538 return requestContextPath;
539 }
540
541 return null;
542 }
543 });
544 }
545
546 private void loadRules(ExternalContext externalContext, String rulesPath) {
547 StringTokenizer st = new StringTokenizer(rulesPath, ",");
548
549 List rules = new ArrayList(32);
550
551 Digester digester = new Digester();
552 fillDigesterRules(digester, rules);
553
554 for (; st.hasMoreTokens();) {
555 String tok = st.nextToken().trim();
556
557 InputStream ins = externalContext.getResourceAsStream(tok);
558 if (ins == null) {
559 LOG.error("Can not open rules resource '" + tok + "'.");
560 continue;
561 }
562
563 try {
564 LOG.info("Open rules resource '" + tok + "'.");
565
566 digester.parse(ins);
567
568 } catch (Exception ex) {
569 LOG.error("Can not parse rules from '" + tok + "'.", ex);
570
571 } finally {
572 try {
573 ins.close();
574 } catch (IOException e) {
575 LOG.error(e);
576 }
577 }
578 }
579
580 if (rules.isEmpty() == false) {
581 this.rules = (IPatternRule[]) rules.toArray(new IPatternRule[rules
582 .size()]);
583 }
584 }
585
586 protected void fillDigesterRules(Digester digester, final List rules) {
587
588 final Map infos = new HashMap();
589
590 digester.addRule("rules/rule", new Rule() {
591
592 public void end(String namespace, String name) throws Exception {
593
594 String url = (String) infos.get("url");
595 Map attributes = null;
596 String pattern = (String) infos.get("pattern");
597 String regexp = (String) infos.get("regexp");
598 infos.clear();
599
600 if (url == null) {
601 LOG.error("No URL defined for rule pattern='" + pattern
602 + "' or regexp='" + regexp + "'.");
603 return;
604 }
605 url = url.trim();
606
607 if (DISABLED_URL.equalsIgnoreCase(url)) {
608 url = DISABLED_URL;
609 }
610
611 if (pattern != null) {
612 pattern = pattern.trim();
613
614 if (pattern.indexOf('*') >= 0) {
615 if (pattern.equals("*")) {
616 rules.add(new DefaultPatternRule(url, attributes));
617 return;
618 }
619
620 if (pattern.indexOf('*') == pattern.length() - 1) {
621 rules.add(new StartsWithRule(url, attributes,
622 pattern));
623 return;
624 }
625
626 if (pattern.lastIndexOf('*') == 0) {
627 rules
628 .add(new EndsWithRule(url, attributes,
629 pattern));
630 return;
631 }
632
633 throw new FacesException(
634 "Invalid expression ! (Use regexp tag to declare more complex pattern) '" + pattern
635 + "'.");
636 }
637
638 rules.add(new BasicPatternRule(url, attributes, pattern));
639 return;
640 }
641
642 if (regexp != null) {
643 regexp = regexp.trim();
644
645 rules.add(new RegExpPatternRule(url, attributes, regexp));
646 return;
647 }
648
649 }
650
651 });
652 digester.addRule("rules/rule/pattern", new Rule() {
653
654 public void body(String namespace, String name, String text)
655 throws Exception {
656 infos.put("pattern", text);
657 }
658 });
659 digester.addRule("rules/rule/regexp", new Rule() {
660
661 public void body(String namespace, String name, String text)
662 throws Exception {
663 infos.put("regexp", text);
664 }
665 });
666 digester.addRule("rules/rule/url", new Rule() {
667
668 public void body(String namespace, String name, String text)
669 throws Exception {
670 infos.put("url", text);
671 }
672 });
673 }
674
675
676
677
678
679
680 protected interface IKeywordValue {
681 String getValue(FacesContext facesContext, String keyword);
682 }
683
684
685
686
687
688
689 protected static interface IPatternRule {
690 String getPattern();
691
692 String getBaseURI();
693
694 Map listAttributes();
695
696 boolean acceptURL(String url);
697 }
698
699
700
701
702
703
704 protected static abstract class AbstractPatternRule implements IPatternRule {
705 protected final String baseURI;
706
707 protected final Map attributes;
708
709 public AbstractPatternRule(String baseURI, Map attributes) {
710 this.baseURI = baseURI;
711
712 if (attributes == null) {
713 attributes = Collections.EMPTY_MAP;
714 }
715 this.attributes = attributes;
716 }
717
718 public String getBaseURI() {
719 return baseURI;
720 }
721
722 public Map listAttributes() {
723 return attributes;
724 }
725
726 public String toString() {
727 return "[Rule baseURI='" + baseURI + "' attributes='" + attributes
728 + "']";
729 }
730 }
731
732
733
734
735
736
737 protected static class BasicPatternRule extends AbstractPatternRule {
738 protected final String pattern;
739
740 public BasicPatternRule(String baseURI, Map attributes, String pattern) {
741 super(baseURI, attributes);
742
743 this.pattern = pattern;
744 }
745
746 public String getPattern() {
747 return pattern;
748 }
749
750 public int hashCode() {
751 return pattern.hashCode();
752 }
753
754 public boolean acceptURL(String url) {
755 return pattern.equals(url);
756 }
757
758 public boolean equals(Object obj) {
759 if (this == obj)
760 return true;
761 if (obj == null)
762 return false;
763 if (getClass() != obj.getClass())
764 return false;
765 BasicPatternRule other = (BasicPatternRule) obj;
766 if (pattern == null) {
767 if (other.pattern != null)
768 return false;
769 } else if (!pattern.equals(other.pattern))
770 return false;
771 return true;
772 }
773
774 public String toString() {
775 return "[BasicRule pattern='" + pattern + "' baseURI='" + baseURI
776 + "' attributes='" + attributes + "']";
777 }
778 }
779
780
781
782
783
784
785 protected static class StartsWithRule extends BasicPatternRule {
786
787 private final String localPattern;
788
789 public StartsWithRule(String baseURI, Map attributes, String pattern) {
790 super(baseURI, attributes, pattern);
791
792 localPattern = pattern.substring(0, pattern.length() - 1);
793 }
794
795 public boolean acceptURL(String url) {
796 return url.startsWith(localPattern);
797 }
798 }
799
800
801
802
803
804
805 protected static class EndsWithRule extends BasicPatternRule {
806
807 private final String localPattern;
808
809 public EndsWithRule(String baseURI, Map attributes, String pattern) {
810 super(baseURI, attributes, pattern);
811
812 localPattern = pattern.substring(1);
813 }
814
815 public boolean acceptURL(String url) {
816 return url.endsWith(localPattern);
817 }
818 }
819
820
821
822
823
824
825 protected static class RegExpPatternRule extends BasicPatternRule {
826
827 private final Pattern localPattern;
828
829 public RegExpPatternRule(String baseURI, Map attributes, String regex) {
830 super(baseURI, attributes, regex);
831
832 localPattern = Pattern.compile(regex);
833 }
834
835 public boolean acceptURL(String url) {
836 Matcher m = localPattern.matcher(url);
837
838 return m.matches();
839 }
840 }
841
842
843
844
845
846
847 protected static class DefaultPatternRule extends BasicPatternRule {
848
849 public DefaultPatternRule(String baseURI, Map attributes) {
850 super(baseURI, attributes, "*");
851 }
852
853 public boolean acceptURL(String url) {
854 return true;
855 }
856 }
857 }