У концепции ADF есть интересное свойство: обязанность по отслеживанию незакоммиченных данных полностью ложится на программиста. Сложно сказать, как с этим планировалось справляться силами недоучившихся студентов, на которых рассчитано декларативное программирование в ADF. Да и не об этом речь. Речь пойдет о том, как уйти со страницы, которая создала новую запись в VO, связанном c EO, не зафиксировать эту запись, и потом благополучно вернуться обратно.
Что сразу приходит в голову:
Рабочее решение заключается в создании бина уровня приложения, унаследованного от PagePhaseListener, где мы будем анализировать страницу на ранней фазе жизненного цикла и отслеживать грязные данные. Создаем бин и прописываем его в adf-settings.xml:
Сам класс:
Код 9 - id фазы инициализаци Restore View. Интересно проследить жизненный цикл страницы-источника (откуда переходим) после изменения данных:
В целевой странице (куда переходим) грязные данные присутствуют во всем цикле:
Что сразу приходит в голову:
@PostConstruct
public void initBean() {
AppModuleImpl mod = (AppModuleImpl)ADFUtils.getBindingApplicationModule();
DBTransaction conn = mod.getDBTransaction();
if (conn.isDirty()) conn.rollback();
...
Так нехорошо, потому что initBean() будет вызываться при каждой активности на странице и транзакция будет теряться тогда, когда это не нужно. К тому же транзакция будет откатываться на фазе prepareRender, что тоже идеологически неверно. То есть контекст уже проинициализирован, загружена модель данных, проверены валидации, всё готово к прорисовке страницы - и тут мы сообщаем системе, что данные нужно изменить. Не знаю, что именно будет происходить глубоко внутри приложения, но думаю, что ничего хорошего.Рабочее решение заключается в создании бина уровня приложения, унаследованного от PagePhaseListener, где мы будем анализировать страницу на ранней фазе жизненного цикла и отслеживать грязные данные. Создаем бин и прописываем его в adf-settings.xml:
Сам класс:
import java.util.ArrayList;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;
import oracle.adf.controller.v2.lifecycle.PagePhaseEvent;
import oracle.adf.controller.v2.lifecycle.PagePhaseListener;
import oracle.adf.share.ADFContext;
import oracle.jbo.server.DBTransaction;
import *.ADFUtils;
import *.AppModuleImpl;
public class PageListenerBean implements PagePhaseListener{
// если conn.isDirty() и переходим на из url из списка, то rollback
private static String[] deniedUrls = {"/myapp/faces/task-flow-definition/tree"};
@Override
public void beforePhase(PagePhaseEvent pagePhaseEvent) {
try{
if (pagePhaseEvent.getPhaseId() == 9){
AppModuleImpl mod = (AppModuleImpl)ADFUtils.getBindingApplicationModule();
DBTransaction conn = mod.getDBTransaction();
if (isPageChanged()){
if (changedToDisallowed()){
if (conn.isDirty()){
conn.rollback();
System.out.println("= rolled back!");
}
}
}
}
}catch(Exception e){
;
}
}
@Override
public void afterPhase(PagePhaseEvent pagePhaseEvent) {
;
}
private void setApplicationScopeParameter(String val){
ADFContext adfCtx = ADFContext.getCurrent();
Map applicationScope = adfCtx.getApplicationScope();
applicationScope.put("currentURL", val);
}
private String getApplicationScopeParameter(){
ADFContext adfCtx = ADFContext.getCurrent();
Map applicationScope = adfCtx.getApplicationScope();
return (String)applicationScope.get("currentURL");
}
private boolean isPageChanged(){
try{
if (!getCurrentUrl().equals(getApplicationScopeParameter())){
setApplicationScopeParameter(getCurrentUrl());
return true;
}
}catch(NullPointerException e){
System.out.println("--! PageListenerBean: can't get current url");
}
return false;
}
private boolean changedToDisallowed(){
for(String url : this.deniedUrls)
if (url.equals(getCurrentUrl()))
return true;
return false;
}
private String getCurrentUrl(){
FacesContext ctx = FacesContext.getCurrentInstance();
HttpServletRequest servletRequest = (HttpServletRequest) ctx.getExternalContext().getRequest();
return servletRequest.getRequestURI();
}
}
При каждом срабатывании проверяется соответствие текущего URL переменной #{applicationScope.currentURL}. Если страница отличается, находится в "стоп-листе" и данные грязные, тогда откатываем транзакцию. Стоп-лист введен во избежание непредвиденных потерь транзакции при единой фиксации нескольких страниц (такое может быть в приложении).Код 9 - id фазы инициализаци Restore View. Интересно проследить жизненный цикл страницы-источника (откуда переходим) после изменения данных:
В целевой странице (куда переходим) грязные данные присутствуют во всем цикле: