2012-03-28 15 views
7

Załóżmy następującą aplikacji krajobraz:JSF. URL rozwiązanie przepisywanie potrzebne

+-----------------+ 
| App server  | 
+-----------------+ 
|     |         +-------+ 
| ear1   |         |  | 
| +-web1 (/ctx1) +--<-- http://localhost/ctx1/xxx/ --+  +--<-- http://www.domain.com/xxx/ 
|     |         |  | 
|     |         | proxy | 
| ear2   |         |  | 
| +-web2 (/ctx2) +--<-- http://localhost/ctx2/yyy/ --+  +--<-- http://abc.domain.com/yyy/ 
|     |         |  | 
+-----------------+         +-------+ 

Jak widać, proxy (nginx w moim przypadku) przekazuje wnioski do do jednej instancji serwera aplikacji, które z kolei ma wiele modułów internetowych z różne ścieżki kontekstowe. Oczywiście nie chcę, aby mój serwer publiczny ujawniał wewnętrzne kontekstowe katalogi, a proxy działa dobrze, owija i rozpakowuje żądania HTTP itd. Ale wciąż jest jeden duży problem: wygenerowany przez JSF kod html (linki, css, zasoby js, formularz akcje) zawiera ścieżki kontekstowe, /ctx1 i /ctx2 w moim przypadku. Tego właśnie chcę uniknąć.

W tej chwili nie mam rozwiązania, z wyjątkiem coraz większej liczby różnych instancji (domen) serwera aplikacji, co powoduje znikanie zasobów sprzętowych. Jak rozumiem, muszę rozszerzyć moje aplikacje JSF o niektóre opakowania, potencjalnie zarejestrowane w faces-config.xml, które usuwają prefiks kontekstowy w wygenerowanym html. Wszelkie inne rozwiązania są również mile widziane.

Proszę wskazać mi właściwy kierunek.

Odpowiedz

4

Zamieszczam rozwiązanie, które może być pomocne dla osób borykających się z tym samym problemem. Wszystko, co potrzebne do zrobienia wdraża własne javax.faces.application.ViewHandler i zarejestrować go w faces-config.xml:

public class CustomViewHandler extends ViewHandlerWrapper { 
    private ViewHandler wrappped; 

    public CustomViewHandler(ViewHandler wrappped) { 
    super(); 
    this.wrappped = wrappped; 
    } 

    @Override 
    public ViewHandler getWrapped() { 
    return wrappped; 
    } 

    @Override 
    public String getActionURL(FacesContext context, String viewId) { 
    String url = super.getActionURL(context, viewId); 
    return removeContextPath(context, url); 
    } 

    @Override 
    public String getRedirectURL(FacesContext context, String viewId, Map<String, List<String>> parameters, boolean includeViewParams) { 
    String url = super.getRedirectURL(context, viewId, parameters, includeViewParams); 
    return removeContextPath(context, url); 
    } 

    @Override 
    public String getResourceURL(FacesContext context, String path) { 
    String url = super.getResourceURL(context, path); 
    return removeContextPath(context, url); 
    } 

    private String removeContextPath(FacesContext context, String url) { 
    ServletContext servletContext = (ServletContext) context.getExternalContext().getContext(); 
    String contextPath = servletContext.getContextPath(); 
    if("".equals(contextPath)) return url; // root context path, nothing to remove 
    return url.startsWith(contextPath) ? url.substring(contextPath.length()) : url; 
    } 
} 

twarze-config.xml:

<faces-config xmlns="http://java.sun.com/xml/ns/javaee" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd" 
       version="2.0"> 
    <application> 
    <view-handler>test.CustomViewHandler</view-handler> 
    </application> 
</faces-config> 
+2

Uwaga: to może być również wykonane z 'Filter' i mniej niż połowa kodu. – BalusC

+0

@BalusC, dzisiaj miałem zamiar zaimplementować twoje rozwiązanie Filter, ale przestraszyłem się, że twój post został usunięty)) – Osw

+0

Ja tylko go skasowałem, ponieważ nie było to warte wysiłku. – BalusC

5

Można użyć OCPsoft Rewrite URLRewriteFilter na to (nie PrettyFaces obecnie, ale można używaj ich obu w tym samym czasie, aż formalnie połączą się po wydaniu PrettyFaces 4 - Rewrite jest głównym projektem dla PrettyFaces 4)

Robienie czegoś takiego powinno być całkiem proste przy użyciu grzechu ustaw regułę konfiguracji. Możesz oczywiście skrzypce, jeśli ta reguła jest zbyt surowa lub zbyt ogólna.

.defineRule() 
.when(URL.matches("{prefix}" + context.getContextPath() + "{suffix}") 
.perform(Substitute.with("{prefix}{suffix}")) 

Sprawdź stronę do przepisywania. Jest bardzo łatwy w konfiguracji. http://ocpsoft.org/rewrite/

+0

Witaj, Lincoln! Czy będzie również działać dla css, js i innych zasobów, które mam na moich stronach? – Osw

+0

Dopóki te linki są renderowane przez JSF, a nie są zakodowane na stałe w kodzie HTML, to tak, absolutnie. W przeciwnym razie możesz skonfigurować dodatkowe reguły obsługi tego scenariusza. – Lincoln

1

Miałem do czynienia z tym samym problemem i wypróbowałem Twoje rozwiązanie. Choć działało mniej więcej, wciąż było kilka usterek. Szczerze mówiąc, bardziej przypomina to walkę z objawami, niż leczenie choroby.

Więc oto co ostatecznie pracował dla mnie:

Zamiast ustawiania wdrożeń siebie przez ścieżkę, ja przypisany każdej wdrożenie do własnego portu:

foo.war <-- http://localhost:8080/ -- | Proxy | <-- http://www.foo.com -- | Client | 
bar.war <-- http://localhost:8181/ -- | Proxy | <-- http://www.bar.com -- | Client | 

ten sposób oba wdrożeń są w stanie use/jako ścieżkę kontekstu, dlatego nie trzeba edytować ścieżki kontekstu.

Aby to osiągnąć, nie trzeba uruchamiać dwóch serwerów aplikacji. W moim przypadku (JBoss Application Server 10.0), to było wystarczające, aby określić dwa serwery cofająca w JBoss Application Server konfiguracji, każdy z własnym wirtualnym hosta i http słuchacza, tak:

<server name="foo-server"> 
    <http-listener name="foo-listener" proxy-address-forwarding="true" socket-binding="foo-http"/> 
    <host name="foo-host" default-web-module="foo.war" alias="localhost, foo.com, wwww.foo.com"/> 
</server> 
<server name="bar-server"> 
    <http-listener name="bar-listener" proxy-address-forwarding="true" socket-binding="bar-http"/> 
    <host name="bar-host" default-web-module="bar.war" alias="localhost, bar.com, wwww.bar.com"/> 
</server> 

<socket-binding name="foo-http" port="${jboss.http.port:8080}"/> 
<socket-binding name="bar-http" port="${jboss.http.port:8181}"/> 

Będziesz również potrzebował JBoss-web.xml w projekcie:

<?xml version="1.0" encoding="UTF-8"?> 
<jboss-web> 
    <server-instance>foo-server</server-instance> 
    <virtual-host>foo-host</virtual-host> 
    <context-root>/</context-root> 
</jboss-web> 

Dwa serwery są potrzebne, ponieważ nie można dodać powiązania gniazda z hostem wirtualnym. Tak więc tutaj jest niewielki narzut, ale nieistotny w porównaniu do uruchomienia dwóch kompletnych serwerów aplikacji.

Edit 1:

To właśnie doszło do mnie, że to chyba nie trzeba nawet używać różnych portów i za pomocą jednego serwera cofająca za deplyoment jest chyba zbędny, jak również.

Ponieważ serwer proxy może przekazywać host zgodnie z żądaniem klienta do serwera aplikacji, program powinien być w stanie wybrać właściwy host wirtualny za pomocą parametru aliasu.

W zasadzie proxy przekazuje dowolne żądanie do strony foo.com lub bar.com do localhost: 8080 i pozwala AS sortować rzeczy.

Nie testowałem tego, ale oto jak to może działać (ponownie, to dla JBoss Application Server 10.0):

<server name="default-server"> 
    <http-listener name="http" proxy-address-forwarding="true" socket-binding="http"/> 
    <host name="foo-host" default-web-module="foo.war" alias="foo.com, wwww.foo.com"/> 
    <host name="bar-host" default-web-module="bar.war" alias="bar.com, wwww.bar.com"/> 
</server> 

I jboss-web.xml straciłaby tag serwera:

<?xml version="1.0" encoding="UTF-8"?> 
<jboss-web> 
    <virtual-host>foo-host</virtual-host> 
    <context-root>/</context-root> 
</jboss-web> 

W przypadku, gdy to zadziała, nie byłoby w ogóle miejsca na koszty ogólne.

Edit 2:

Właśnie testowałem uproszczone podejście - Tak, to działa :)