Как мы знаем, ограниченный поток задач может быть вызван по некоторому 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> |
Это оно!

