Как мы знаем, ограниченный поток задач может быть вызван по некоторому URL либо напрямую из браузера, либо из какого-либо внешнего приложения. Эта функция включена, если для свойства потока задач «URL invoke» установлено значение «url-invoke-разрешено», и оно обычно используется в интеграционных проектах. Обычно клиенты (или инициаторы) используют метод HTTP GET и передают свои параметры в URL. Давайте рассмотрим простой поток задач с одним обязательным входным параметром:
1
2
3
4
5
6
7
8
|
<task-flow-definition id= "task-flow-definition" > <input-parameter-definition id= "__23" > <name id= "__24" >userName</name> <value id= "__67" >#{requestScope.userName}</value> < class id= "__63" >java.lang.String</ class > <required/> </input-parameter-definition> ... |
Поток задач может быть вызван следующим URL
1
|
http: //127.0.0.1:7101/TestApp/faces/adf.task-flow?adf.tfId=task-flow-definition&adf.tfDoc=/WEB-INF/task-flow-definition.xml&userName=xammer |
Клиент использует простую HTML-форму для создания этого запроса GET:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
<html> <head> <meta http-equiv= "Content-Type" content= "text/html; charset=UTF-8" /> </head> <body> <input type= "hidden" name= "adf.tfId" value= "task-flow-definition" /> <input type= "hidden" name= "adf.tfDoc" value= "/WEB-INF/task-flow-definition.xml" /> <label> User Name <input type= "text" name= "userName" value= "xammer" /> </label> <input type= "submit" value= "Submit" /> </form> </body> </html> |
И это выглядит так:
Некоторые клиенты предпочитают использовать метод HTTP POST, и, кроме того, это их требование:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
<html> <head> <meta http-equiv= "Content-Type" content= "text/html; charset=UTF-8" /> </head> <body> <input type= "hidden" name= "adf.tfId" value= "task-flow-definition" /> <input type= "hidden" name= "adf.tfDoc" value= "/WEB-INF/task-flow-definition.xml" /> <label> User Name <input type= "text" name= "userName" value= "xammer" /> </label> <input type= "submit" value= "Submit" /> </form> </body> </html> |
И это прекрасно работает. URL в этом случае будет выглядеть так:
1
|
http: //127.0.0.1:7101/TestApp/faces/adf.task-flow |
Вся остальная необходимая информация, такая как идентификатор потока задач и значение параметра, находится внутри запроса POST. Но проблема в том, что он работает нормально только для R1. Если мы попробуем это на R2, мы получим следующее:
ADF_FACES-30179: Дополнительные сведения см. В журнале ошибок сервера для записи, начинающейся с: UIViewRoot имеет значение null. Неустранимое исключение во время PhaseId: RESTORE_VIEW 1.
Почему? Из-за этого:
1
2
3
4
5
6
|
oracle.adfinternal.controller.application.InvokeTaskFlowException: ADFC-02006: A task flow ID is not found in the URL. at oracle.adfinternal.controller.util.UrlParams.getTaskFlowInfo(UrlParams.java:144) at oracle.adfinternal.controller.application.RemoteTaskFlowCallRequestHandler. invokeTaskFlowByUrl(RemoteTaskFlowCallRequestHandler.java:84) at oracle.adfinternal.controller.application.RemoteTaskFlowCallRequestHandler. doCreateView(RemoteTaskFlowCallRequestHandler.java:63) |
Все необходимые данные, включая идентификатор потока задач, который должен быть передан внутри запроса POST, теряются. Почему? Из-за «петли». Если мы обнаружим запросы, отправленные из браузера на сервер при нажатии кнопки «Отправить», мы увидим следующее:
Таким образом, вместо отправки «честного» ответа сервер отправляет некоторый «петлевой» скрипт, который генерирует «идентификатор окна» и отправляет следующий запрос GET с созданным идентификатором окна. Здорово! Но все почтовые данные исчезли. Запрос GET абсолютно пустой.
К счастью, фреймворк не генерирует никаких «петель», если исходный запрос POST уже имеет некоторый «идентификатор окна». Итак, обходной путь для нашего случая — разработать фильтр сервлета, установив атрибут «id окна» для нашего запроса:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest r = (HttpServletRequest) servletRequest; HttpSession s = r.getSession(); //May be this is not an initial request and window id has been generated earlier //We want all the following requests to work with the same window id //For our use-case this is ok String windowID = (String) s.getAttribute(_WINDOW_ID_KEY); if (windowID == null ) { String pathInfo = r.getPathInfo(); //This is an initial POST request to get access to the task flow if (( "/adf.task-flow" ).equals(pathInfo) && "POST" .equals(r.getMethod())) { windowID = WINDOW_ID; //Save window id in the session s.setAttribute(_WINDOW_ID_KEY, windowID); } } //Setup attribute for the request //This will prevent generating of the loopback if (windowID != null ) r.setAttribute(_WINDOW_ID_KEY, windowID); filterChain.doFilter(servletRequest, servletResponse); } private static final String __WINDOW_MANAGER_KEY = RichWindowManager. class .getName(); private static final String _WINDOW_ID_KEY = __WINDOW_MANAGER_KEY + "#WINDOW_ID" ; private static final String WINDOW_ID = "wextflow" ; |
Обратите внимание, что этот фильтр должен стоять перед фильтром «Тринидад» в цепочке фильтров:
01
02
03
04
05
06
07
08
09
10
11
12
|
<filter> <filter-name>ExtPostFilter</filter-name> <filter- class >com.cs.fusion.core.view.filter.ExtPostFilter</filter- class > </filter> <filter> <filter-name>trinidad</filter-name> <filter- class >org.apache.myfaces.trinidad.webapp.TrinidadFilter</filter- class > </filter> <filter> <filter-name>ServletADFFilter</filter-name> <filter- class >oracle.adf.share.http.ServletADFFilter</filter- class > </filter> |
Это оно!