[ 
https://issues.apache.org/jira/browse/MYFACES-2730?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=12882004#action_12882004
 ] 

Leonardo Uribe commented on MYFACES-2730:
-----------------------------------------

I review again all the problem and finally I notice the missing arguments.

The lines that cause the problem are these (see AbstractFacesInitializer class):

    private void dispatchInitDestroyEvent(ServletContext servletContext, Class 
eventClass) {
        ApplicationFactory appFac = (ApplicationFactory) 
FactoryFinder.getFactory(FactoryFinder.APPLICATION_FACTORY);
        FacesContext fc = null;

        fc = FacesContext.getCurrentInstance();
        if (fc == null) {
            LifecycleFactory lifeFac = (LifecycleFactory) 
FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
            FacesContextFactory facFac = (FacesContextFactory) 
FactoryFinder.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
            fc = facFac.getFacesContext(servletContext, 
                    new _SystemEventServletRequest(), 
                    new _SystemEventServletResponse(), 
                    lifeFac.getLifecycle(getLifecycleId(servletContext)));
        }
        
        // in order to allow FacesContext.getViewRoot calls during 
startup/shutdown listeners, 
        // we need to initialize a new ViewRoot with locale set to 
Locale.getDefault().
        UIViewRoot root = new UIViewRoot();
        root.setLocale(Locale.getDefault());
        fc.setViewRoot(root);
        
        appFac.getApplication().publishEvent(fc, eventClass, Application.class, 
appFac.getApplication());
    }

In order to propagate init and destroy events we try to lookup the 
FacesContextFactory instance and create a FacesContext object just for that 
purpose. With the changes proposed, that lookup just dissapear, because it is 
replaced by StartupFacesContext.

 Myfaces orchestra and trinidad installs its own FacesContext wrapper, and in 
those methods, initialization stuff happen. See for example trinidad 
(FacesContextFactoryImpl):

  public FacesContext getFacesContext(
      Object context,
      Object request,
      Object response, 
      Lifecycle lifecycle)
  {
    return new CacheRenderKit(_factory.getFacesContext(context, request, 
response, lifecycle));
  }
  
  static public class CacheRenderKit extends FacesContext
  {
    @SuppressWarnings("unchecked")
    public CacheRenderKit(FacesContext base)
    {
      _base = base;
      ExternalContext baseExternal = base.getExternalContext();
      GlobalConfiguratorImpl config = GlobalConfiguratorImpl.getInstance();

      //This should be done only if beginRequest was not called on the 
configurator
      //before we retrieve the FacesContext.  If this is the case then we'll 
need to handle
      //cleanup on the release of the FacesContext.  Otherwise the endRequest 
should be
      //called by whatever did he origional beginRequest.
      if(!GlobalConfiguratorImpl.isRequestStarted(baseExternal))
      {
        Map<String, Object> requestMap = baseExternal.getRequestMap();
        requestMap.put(_CONFIG_IN_CONTEXT, Boolean.TRUE);
      }

      _external = new OverrideDispatch(config.getExternalContext(baseExternal));
      setCurrentInstance(this);
    }

Now look this stack trace from MYFACES-2520.

at 
org.apache.myfaces.context.servlet.ServletExternalContextImpl.getRequestContentType(ServletExternalContextImpl.java:322)
at 
org.apache.myfaces.trinidad.util.ExternalContextUtils.getContentType(ExternalContextUtils.java:341)
at 
org.apache.myfaces.trinidadinternal.share.util.MultipartFormHandler.isMultipartRequest(MultipartFormHandler.java:57)
at 
org.apache.myfaces.trinidadinternal.config.upload.FileUploadConfiguratorImpl.beginRequest(FileUploadConfiguratorImpl.java:109)
at 
org.apache.myfaces.trinidadinternal.config.GlobalConfiguratorImpl._startConfiguratorServiceRequest(GlobalConfiguratorImpl.java:532)
at 
org.apache.myfaces.trinidadinternal.config.GlobalConfiguratorImpl.beginRequest(GlobalConfiguratorImpl.java:211)
at 
org.apache.myfaces.trinidadinternal.config.GlobalConfiguratorImpl.getExternalContext(GlobalConfiguratorImpl.java:327)
at 
org.apache.myfaces.trinidadinternal.context.FacesContextFactoryImpl$CacheRenderKit.<init>(FacesContextFactoryImpl.java:90)
at 
org.apache.myfaces.trinidadinternal.context.FacesContextFactoryImpl.getFacesContext(FacesContextFactoryImpl.java:68)
 

I was supposing this error was because there was a lookup for RenderKitFactory, 
but after checking the code, the conclusion is that's not true.

So, which factories should not be called during initialization, because they 
are susceptible to have code that require request/response objects?

FacesContextFactory
ExternalContextFactory (added on jsf 2.0)
PartialViewContextFactory (added on jsf 2.0)
VisitContextFactory (added on jsf 2.0)

All other factories are safe, because from its constructors or its methods 
there is no way to get request or response object without go through 
FacesContext.getCurrentInstance(). Fortunately, with the fix proposed those 
factories will not be lookup during setup or shutdown time. Since we are not 
using FacesContextFactory to create FacesContext instances, there is just no 
chance that we could call methods that requires request/response objects during 
initialization, so there will not be compatibility problems with old jsf 
libraries, because we will not call susceptible methods from those locations.

So, with the recently found evidence we can conclude the following (see 
proposed solution MYFACES-2730-4.patch):

- It is not necessary to use dummy methods on StartupFacesContextImpl and 
StartupServletExternalContextImpl, because there is no jsf 1.1 or jsf 1.2 code 
that requires dummy request/response objects. The reason about why there is no 
susceptible code is FacesContext is not set on setup/shutdown, and there is no 
way to access to request or response object. In jsf 1.1 or jsf 1.2, the common 
workaroud was use custom wrappers but located on the filter used (see trinidad 
filter for example), and initialize on the first request.

- All methods that are not related to request/response objects are required to 
be implemented. The list of methods to be implement is this:

StartupFacesContextImpl

public ExceptionHandler getExceptionHandler()
public void setExceptionHandler(ExceptionHandler exceptionHandler)
public final ExternalContext getExternalContext()
public Application getApplication()
public final RenderKit getRenderKit()
public final void setViewRoot(final UIViewRoot viewRoot) //For event processing
public final UIViewRoot getViewRoot()                    //For event processing
public final ELContext getELContext()
public Map<Object, Object> getAttributes()
public boolean isProcessingEvents()                       //For event processing
public void setProcessingEvents(boolean processingEvents) //For event processing
public void release()

StartupServletExternalContextImpl

public Map<String, Object> getApplicationMap()
public String getMimeType(String file)
public Object getContext()
public String getContextName()
public String getInitParameter(final String s)
public Map<String, String> getInitParameterMap()
public URL getResource(final String path) throws MalformedURLException
public InputStream getResourceAsStream(final String path)
public Set<String> getResourcePaths(final String path)
public void log(final String message)
public void log(final String message, final Throwable exception)
public String getRealPath(String path)
public void release()

   The remaining methods could throw 
UnsupportedOperationException/IllegalStateException.

I'm happy with the latest solution, because it is supported with all evidence 
and does not have any hidden problems. If no objections I'll commit this patch 
soon.



> FacesContext not available on application startup
> -------------------------------------------------
>
>                 Key: MYFACES-2730
>                 URL: https://issues.apache.org/jira/browse/MYFACES-2730
>             Project: MyFaces Core
>          Issue Type: Bug
>          Components: JSR-127, JSR-252, JSR-314
>    Affects Versions: 1.1.8, 1.2.9, 2.0.0
>            Reporter: Nick Belaevski
>            Assignee: Leonardo Uribe
>             Fix For: 2.0.2-SNAPSHOT
>
>         Attachments: MYFACES-2730-1.patch, MYFACES-2730-2.patch, 
> MYFACES-2730-3.patch, MYFACES-2730-4.patch, MYFACES-2730-revert.patch
>
>
> If custom ResourceHandler calls FacesContext.getCurrentInstance() in 
> constructor to read init parameters, null value is returned. This affects 
> latest MyFaces 2.0.0-SNAPSHOT. Mojarra 2.0 provides InitFacesContext in this 
> case.

-- 
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.

Reply via email to