FacesContext fctx = FacesContext.getCurrentInstance();
Application app = fctx.getApplication();
ExpressionFactory elFactory = app.getExpressionFactory();
ELContext elCtx = fctx.getELContext();
String el = "#{pageData}";
ValueExpression ve = elFactory.createValueExpression(elCtx, el, Object.class);
Object bean = ve.getValue(elCtx);
The problem was that the very last line was always returning null! Our pageData bean was originally scoped as "pageFlow", but setting it to "session", or "application", or "request" allowed the code to work. The only two scopes that did not work were "pageFlow" and "view". In fact our page that was referencing this bean with EL was also not seeing it when the bean was scoped this this way.
Turns out that unlike normal JSF scopes, ADF scopes require the scope to be specified when accessing the bean. So we needed to change our EL statement from #{pageData} to #{pageFlowScope.pageData}. In both the code and on the page this resolved our issue.
What notified me of this odd behavior was a small note in the ADF documentation here:
Note:
Write EL expressions that explicitly qualify the scope to access when writing EL expressions to access custom scopes unique to Oracle ADF (pageFlow, backingBean, and view scopes). For example, write an EL expression to access a pageFlow scope as follows:#{pageFlowScope.inpTxtBB.uiComponent}
Personally, I find this behavior somewhat counterproductive, because you are going to need to find all references whenever you change a bean's scope (which will likely only happen in initial development). Given that EL is very silent about missing references (by design), this increases the risk of broken code. Hopefully it will not be too big of a deal in the future.