View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.myfaces.tobago.internal.ajax;
21  
22  import org.apache.myfaces.tobago.util.AjaxUtils;
23  import org.slf4j.Logger;
24  import org.slf4j.LoggerFactory;
25  
26  import javax.faces.application.FacesMessage;
27  import javax.faces.application.ViewExpiredException;
28  import javax.faces.component.UIViewRoot;
29  import javax.faces.context.ExternalContext;
30  import javax.faces.context.FacesContext;
31  import javax.faces.event.ExceptionQueuedEvent;
32  import java.io.IOException;
33  import java.util.ArrayList;
34  import java.util.HashMap;
35  import java.util.Iterator;
36  import java.util.List;
37  import java.util.Map;
38  
39  /**
40   * XXX this must be refactored, because of TOBAGO-1524 (see TOBAGO-1563)
41   * @deprecated
42   */
43  @Deprecated
44  public final class AjaxNavigationState {
45  
46    private static final Logger LOG = LoggerFactory.getLogger(AjaxNavigationState.class);
47  
48    private static final String SESSION_KEY = "tobago-AjaxNavigationState";
49  
50    private static final String VIEW_ROOT_KEY = "tobago-AjaxNavigationState-VIEW_ROOT_KEY";
51  
52    private UIViewRoot viewRoot;
53  
54    private Map<String, List<FacesMessage>> messages;
55  
56    private AjaxNavigationState(final FacesContext facesContext) {
57      final ExternalContext externalContext = facesContext.getExternalContext();
58      externalContext.getSessionMap().put(SESSION_KEY, this);
59      viewRoot = facesContext.getViewRoot();
60      messages = new HashMap<String, List<FacesMessage>>();
61      final Iterator<String> iterator = facesContext.getClientIdsWithMessages();
62      while (iterator.hasNext()) {
63        addFacesMessages(facesContext, iterator.next());
64      }
65      if (LOG.isTraceEnabled()) {
66        LOG.trace("Saved viewRoot.getViewId() = \"{}\"", viewRoot.getViewId());
67        for (final Map.Entry<String, List<FacesMessage>> entry : messages.entrySet()) {
68          for (final FacesMessage message : entry.getValue()) {
69            LOG.trace("Saved message \"{}\" : \"{}\"", entry.getKey(), message);
70          }
71        }
72      }
73    }
74  
75    private void addFacesMessages(final FacesContext facesContext, final String clientId) {
76      final Iterator<FacesMessage> facesMessages = facesContext.getMessages(clientId);
77      while (facesMessages.hasNext()) {
78        addFacesMessage(clientId, facesMessages.next());
79      }
80    }
81  
82    private void addFacesMessage(final String clientId, final FacesMessage facesMessage) {
83      List<FacesMessage> facesMessages = messages.get(clientId);
84      if (facesMessages == null) {
85        facesMessages = new ArrayList<FacesMessage>();
86        messages.put(clientId, facesMessages);
87      }
88      facesMessages.add(facesMessage);
89    }
90  
91    private void restoreView(final FacesContext facesContext) {
92      facesContext.setViewRoot(viewRoot);
93      for (final Map.Entry<String, List<FacesMessage>> entry : messages.entrySet()) {
94        for (final FacesMessage facesMessage : entry.getValue()) {
95          facesContext.addMessage(entry.getKey(), facesMessage);
96        }
97      }
98      facesContext.renderResponse();
99      if (LOG.isTraceEnabled()) {
100       LOG.trace("Restored viewRoot.getViewId() = \"{}\"", viewRoot.getViewId());
101       for (final Map.Entry<String, List<FacesMessage>> entry : messages.entrySet()) {
102         for (final FacesMessage message : entry.getValue()) {
103           LOG.trace("Restored message \"{}\" : \"{}\"", entry.getKey(), message);
104         }
105       }
106     }
107 
108   }
109 
110   public static void storeIncomingView(final FacesContext facesContext) {
111     final UIViewRoot viewRoot = facesContext.getViewRoot();
112     if (LOG.isTraceEnabled()) {
113       if (viewRoot != null) {
114         LOG.trace("incoming viewId = '{}'", viewRoot.getViewId());
115       } else {
116         LOG.trace("incoming viewRoot is null");
117       }
118     }
119     facesContext.getExternalContext().getRequestMap().put(AjaxNavigationState.VIEW_ROOT_KEY, viewRoot);
120   }
121 
122   public static boolean isNavigation(final FacesContext facesContext) {
123 
124     final UIViewRoot viewRoot = facesContext.getViewRoot();
125     if (viewRoot != null) {
126       if (LOG.isDebugEnabled()) {
127         LOG.debug("current viewId = '{}'", viewRoot.getViewId());
128       }
129     } else {
130       LOG.warn("current viewRoot is null");
131       return false;
132     }
133 
134     final Map<String, Object> requestMap = facesContext.getExternalContext().getRequestMap();
135     final UIViewRoot incomingViewRoot = (UIViewRoot) requestMap.get(VIEW_ROOT_KEY);
136     if (viewRoot != incomingViewRoot) {
137       if (LOG.isDebugEnabled()) {
138         LOG.debug("requesting full page reload because of navigation to {} from {}",
139             viewRoot.getViewId(), incomingViewRoot.getViewId());
140       }
141       return true;
142     }
143     return false;
144   }
145 
146   public static void beforeRestoreView(final FacesContext facesContext) {
147     if (facesContext.getExternalContext().getSessionMap().get(AjaxNavigationState.SESSION_KEY) != null) {
148       // restoreView phase and afterPhaseListener are executed even if renderResponse is set
149       // so we can't call navigationState.restoreView(...) here in before Phase
150       // set empty UIViewRoot to prevent executing restore state logic
151       facesContext.setViewRoot(new UIViewRoot());
152     }
153   }
154 
155   public static void afterRestoreView(final FacesContext facesContext) {
156     if (isViewExpiredExceptionThrown(facesContext)) {
157       try {
158         facesContext.getExceptionHandler().handle();
159       } catch (ViewExpiredException e) {
160         LOG.debug("Caught: " + e.getMessage(), e);
161         try {
162           final ExternalContext externalContext = facesContext.getExternalContext();
163           final String url = externalContext.getRequestContextPath()
164                        + externalContext.getRequestServletPath() + externalContext.getRequestPathInfo();
165           AjaxInternalUtils.redirect(facesContext, url);
166           facesContext.responseComplete();
167         } catch (IOException e1) {
168           LOG.error("Caught: " + e1.getMessage(), e);
169         }
170       }
171     }
172 
173     final ExternalContext externalContext = facesContext.getExternalContext();
174     if (externalContext.getSessionMap().get(AjaxNavigationState.SESSION_KEY) == null) {
175       storeIncomingView(facesContext);
176     } else {
177       final AjaxNavigationState navigationState
178           = (AjaxNavigationState) externalContext.getSessionMap().remove(AjaxNavigationState.SESSION_KEY);
179       navigationState.restoreView(facesContext);
180       LOG.trace("force render requested navigation view");
181     }
182   }
183 
184   private static boolean isViewExpiredExceptionThrown(FacesContext facesContext) {
185     final Iterator<ExceptionQueuedEvent> eventIterator
186         = facesContext.getExceptionHandler().getUnhandledExceptionQueuedEvents().iterator();
187     if (eventIterator.hasNext()) {
188       Throwable throwable = eventIterator.next().getContext().getException();
189       if (throwable instanceof ViewExpiredException) {
190         return true;
191       }
192     }
193     return false;
194   }
195 
196   public static void afterInvokeApplication(FacesContext facesContext) {
197     if (AjaxUtils.isAjaxRequest(facesContext) && isNavigation(facesContext)) {
198       try {
199         facesContext.getExternalContext().getSessionMap().put(SESSION_KEY, new AjaxNavigationState(facesContext));
200         AjaxInternalUtils.requestNavigationReload(facesContext);
201       } catch (IOException e) {
202         LOG.error("Caught: " + e.getMessage(), e);
203       }
204     }
205   }
206 }