1

我是一名应用程序程序员,正在开发我的第一个 JSF 2.0 网站,我承认我对 JSF 的了解并不多。几个月来我一直在研究文件,特别是感谢这些论坛,我还没有坚持到这一点。大部分网站已经完成并且可以正常工作,这里使用的支持 bean 在其他地方使用也没有问题。

我有一个序列化的搜索条件对象,需要通过 servlet 从小程序提交到支持 bean。支持 bean 方法然后处理搜索条件数据并将其应用于产品列表,保存在数据库中,然后在新的 JSF 页面中显示排名产品列表。

使用正确数据打开结果页面的所有尝试均失败。导航案例 "return "process_MainSearchResult";" 没有在支持 bean 中做任何事情(进一步参见支持 bean 代码和 faces-config 条目)。我尝试使用 appletContext.showDocument(见下文)从小程序打开结果页面,但新页面没有搜索条件对象传递到的支持 bean,因此没有正确的数据。

注意在小程序的setRequestMethod上设置POST是没有效果的;它总是使用服务。如果 setDoInput 和 setDoOutput 未设置为 true,并且从 servlet 向 applet 发送响应,则不会发送提交的对象,并且不会发生任何事情。我怀疑这是我问题的核心,但以任何方式更改它都会阻止序列化对象成功提交。

就目前而言,对象已成功发送到支持 bean,但新页面不会加载正确的数据(使用 applet 中的 showDocument 而不是 servlet 中的重定向)。有没有办法在新网页上设置原始支持 bean,还是我做错了?

Web 服务器是 Glassfish 3.x,IDE 是 Netbeans 7.0.1,系统是 WinXP。支持 bean 类是“ProductSelection”;servlet 类是“CriteriaServlet”。

小程序“提交搜索条件”按钮代码:

private void jButton8ActionPerformed(java.awt.event.ActionEvent evt)                                         
{                                             
    criteriaModel.loadCodeBase();
    int choice = JOptionPane.showConfirmDialog(this,
         "Are you sure you want to submit your search criteria and exit the \"Customise Search Criteria\" web page?",
         "Confirm Submit",
         JOptionPane.YES_NO_OPTION,
         JOptionPane.QUESTION_MESSAGE);

     if (choice == 0)
     {
         try 
         {      
             URL url;

             url = new URL(criteriaModel.getCodeBase(), "CriteriaServlet");

             System.out.println("Servlet address is: " + url);

            // Get the search criteria object. 
            Object searchSubmitObject = criteriaModel.getObjectSlideData();

            HttpURLConnection connection = (HttpURLConnection)url.openConnection();
            connection.setDoInput(true);
            connection.setDoOutput(true);
            connection.setUseCaches(false);
            connection.setRequestMethod("POST");
            connection.setRequestProperty("Content-Type","application/x-java-serialized-object");

            ObjectOutputStream out = new ObjectOutputStream(connection.getOutputStream());
            out.writeObject(searchSubmitObject);
            out.flush();
            out.close();
            out.close();

            System.out.println("Object Written");

            // If this and the corresponding servlet response code is removed
            // then the searchSubmitObject fails to be sent.
            ObjectInputStream in = new ObjectInputStream(connection.getInputStream());
            String response = (String)in.readObject();
            System.out.println(response);
            in.close();
         } 
         catch (MalformedURLException ex)
        {
            JOptionPane.showMessageDialog(jPanel8, "Submit criteria file Malformed URL."
                    + ex.toString());
            System.out.println("MalformedURLException occurred");
            Logger.getLogger(CriteriaInterfaceView.class.getName()).log(Level.SEVERE, null, ex);
        }
        catch (Exception e) 
         {
             System.out.println("Submit criteria file ERROR exception: " + e.toString());
             JOptionPane.showMessageDialog(jPanel8, "Submit criteria file ERROR exception:"
                    + e.toString());
         }
     }

    // This opens a new page but with a new backing bean with the wrong data.
    try
    {
        appletContext.showDocument(new URL(criteriaModel.getCodeBase()+"MainSearchResult.xhtml"),"_SELF");
    }
    catch (MalformedURLException ex)
    {
        Logger.getLogger(CriteriaInterfaceView.class.getName()).log(Level.SEVERE, null, ex);
    }
}   

我尝试使用 redirect(url) 在 servlet 中重定向但没有成功:

@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
{
    System.out.println("service(ServletRequest req, ServletResponse res)");

    res.setContentType("application/x-java-serialized-object");

    try
    {
        ObjectInputStream in = new ObjectInputStream(req.getInputStream());
        slideData = (MultipleSlideDataObject2)in.readObject();
        in.close();

        if(slideData != null)
        {
            System.out.println("Serial number of submitted slide series is: " + slideData.getSerialNumber());
        }

        String temp = "Criteria file Recieved";
        ObjectOutputStream outputToApplet = new ObjectOutputStream(res.getOutputStream());
        outputToApplet.writeObject(temp);
        outputToApplet.flush();          
        outputToApplet.close();
    }
    catch (ClassNotFoundException ex)
    {
        Logger.getLogger(CriteriaServlet.class.getName()).log(Level.SEVERE, null, ex);
    }

    FacesContext facesContext = FacesUtil.getFacesContext(req, res);
    // Get the backing bean.
    ProductSelection productSelection = (ProductSelection) facesContext.getApplication().evaluateExpressionGet(facesContext, "#{productSelection}", ProductSelection.class);
    productSelection.submitSearchCriteriaFile(slideData);

    // This throws an java.lang.IllegalStateException error.
    try 
    {
        FacesContext context = FacesContext.getCurrentInstance();

        ExternalContext extContext = context.getExternalContext();
        String url = extContext.encodeActionURL(context.getApplication().getViewHandler().getActionURL(context, "/MainSearchResult.xhtml"));

        extContext.redirect(url);
    } 
    catch (IOException e) 
    {
        throw new FacesException(e);
    }

给出以下错误,因为我怀疑当前响应已经提交:

警告:StandardWrapperValve [CriteriaServlet]:PWC1406:Servlet CriteriaServlet 的 Servlet.service() 在 com.sun.faces 的 org.apache.catalina.connector.ResponseFacade.sendRedirect(ResponseFacade.java:522) 处抛出异常 java.lang.IllegalStateException。 context.ExternalContextImpl.redirect(ExternalContextImpl.java:572) 在 searchselection.CriteriaServlet.service(CriteriaServlet.java:217) 在 org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1523) 在 org.apache.catalina .core.StandardWrapperValve.invoke(StandardWrapperValve.java:279) 在 org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:188) 在 org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:641 ) 在 com.sun 的 com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:97)。Enterprise.web.PESessionLockingStandardPipeline.invoke(PESessionLockingStandardPipeline.java:85) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:185) at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java: 325) 在 com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:165) 在 com.sun.grizzly 的 org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:226) .http.ProcessorTask.invokeAdapter(ProcessorTask.java:791) 在 com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:693) 在 com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:954 ) 在 com.sun.grizzly.DefaultProtocolChain 的 com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:170)。executeProtocolFilter(DefaultProtocolChain.java:135) 在 com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:102) 在 com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:88) 在 com.sun.grizzly.http .HttpProtocolChain.execute(HttpProtocolChain.java:76) 在 com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:53) 在 com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:57) 在 com.sun。 com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:330) 在 com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool. java:309) 在 java.lang.Thread.run(Thread.java:619)在 com.sun.grizzly 执行(DefaultProtocolChain.java:102)在 com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:88) 在 com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:76) 在 com.sun.grizzly .ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:53) 在 com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:57) 在 com.sun.grizzly.ContextTask.run(ContextTask.java:69) 在 com.sun。 grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:330) at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:309) at java.lang.Thread.run(Thread.java: 619)在 com.sun.grizzly 执行(DefaultProtocolChain.java:102)在 com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:88) 在 com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:76) 在 com.sun.grizzly .ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:53) 在 com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:57) 在 com.sun.grizzly.ContextTask.run(ContextTask.java:69) 在 com.sun。 grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:330) at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:309) at java.lang.Thread.run(Thread.java: 619)76) 在 com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:53) 在 com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:57) 在 com.sun.grizzly.ContextTask.run(ContextTask.java :69) 在 com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:309) 在 java.lang 的 com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:330)。线程运行(线程.java:619)76) 在 com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:53) 在 com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:57) 在 com.sun.grizzly.ContextTask.run(ContextTask.java :69) 在 com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:309) 在 java.lang 的 com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:330)。线程运行(线程.java:619)在 java.lang.Thread.run(Thread.java:619) 处运行(AbstractThreadPool.java:309)在 java.lang.Thread.run(Thread.java:619) 处运行(AbstractThreadPool.java:309)

返回“process_MainSearchResult”;在 backing bean 中不起作用:

public String submitSearchCriteriaFile(MultipleSlideDataObject2 slideData) throws IOException
{
    System.out.println("Recieved slide series with serial number: " + slideData.getSerialNumber());

    // If there is no slide data then...
    if (slideData == null)
    {
        return "process_MainSearchResultFailed";
    }
    else
    {
        rankProducts(slideData);
    }

    rowStart = 0;
    currentStartPage = 0;
    currentPageIndex = 0;
    calculateNumberPages();
    SetupPaginationValues();

    // Ignores this...
    return "process_MainSearchResult";
}

Faces-config.xml 条目:

<navigation-rule>
    <navigation-case>
        <from-outcome>process_MainSearchResult</from-outcome>
        <to-view-id>/MainSearchResult.xhtml</to-view-id>
    </navigation-case>
</navigation-rule>

我也在支持 bean 中尝试过这个来强制重定向:

FacesContext context = FacesContext.getCurrentInstance();
    HttpServletResponse response = (HttpServletResponse)context.getExternalContext().getResponse();

    try
    {
        response.sendRedirect("MainSearchResult.xhtml");
        //response.redirect("http://localhost:8080/SearchEngineServer/faces/MainSearchResult.xhtml");
    }
    catch (IOException e)
    {
        e.printStackTrace();
    }

    return null;

还有这个在backing bean中:

        redirectToPage("/MainSearchResult.xhtml");

调用此方法:

private void redirectToPage(String toUrl) 
{
    try 
    {
        FacesContext ctx = FacesContext.getCurrentInstance();

        ExternalContext extContext = ctx.getExternalContext();
        String url = extContext.encodeActionURL(ctx.getApplication().getViewHandler().getActionURL(ctx, toUrl));

        extContext.redirect(url);
    } 
    catch (IOException e) 
    {
        throw new FacesException(e);
    }
}

都给出与上面给出的 servlet 示例相同的 java.lang.IllegalStateException 错误。重定向的文档说明了这一点:

IllegalStateException - if, in a portlet environment, the current response object is a RenderResponse instead of an ActionResponse 
IllegalStateException - if, in a servlet environment, the current response has already been committed 
4

2 回答 2

2

重定向失败,因为您在 servlet 中执行重定向之前已经编写并提交了响应。您似乎认为您可以在一个请求上发送多个响应。这实际上是不正确的。每个 HTTP 请求只能发回一个 HTTP 响应,不能更多。从该FacesUtil#getFacesContext()行开始删除整个块。它不属于那里。

我不确定那个 servlet 在做什么,它似乎没有做任何有用的事情,但是您应该AppletContext#showDocument()在调用 servlet 之后让 applet 本身执行“重定向”。您可以以通常的方式将搜索条件(您尝试序列化的 Java 对象实例的属性)作为 GET 请求参数传递给 JSF 页面/bean,并让 JSF 通过 or 收集它并通过or@ManagedProperty处理<f:viewParam>它。@PostConstruct<f:event>

例如

String query = "?param1=" + URLEncoder.encode(param1, "UTF-8")
             + "&param2=" + URLEncoder.encode(param2, "UTF-8")
             + "&param3=" + URLEncoder.encode(param3, "UTF-8");

getAppletContext().showDocument(new URL(getCodeBase(), "MainSearchResult.xhtml" + query), "_SELF");

_

@ManagedBean
@RequestScoped
public class ProductSelection {

    @ManagedProperty("#{param.param1}")
    private String param1;

    @ManagedProperty("#{param.param2}")
    private String param2;

    @ManagedProperty("#{param.param3}")
    private String param3;

    @PostConstruct
    public void init() {
        // Do your business job based on the submitted request parameters.
    }

    // ...
}

或者

<f:metadata>
    <f:viewParam name="param1" value="#{productSelection.param1}" />
    <f:viewParam name="param2" value="#{productSelection.param2}" />
    <f:viewParam name="param3" value="#{productSelection.param3}" />
    <f:event type="preRenderView" listener="#{productSelection.init}" />
</f:metadata>

当以这种方式实现它时,我认为带有所有 Java 序列化的笨拙 servlet 步骤是完全多余的。您可以将其删除。此外,通过这种方式,您最终会得到一个很好的书签、可重用和可搜索机器人索引的页面,该页面可以独立于 applet/servlet 打开。

也可以看看:

于 2012-09-04T10:48:33.763 回答
0

BalusC 的答案是少量对象数据的适当答案,但是,我提交给支持 bean 的对象是 2.2 兆字节,不适合在 URL 中编码。此外,我不希望人们将此特定页面添加为书签,因为将来可能会将更多属性添加到搜索条件文件中,这会使书签属性无效。

我使用的解决方案技术含量很低,但它确实有效。applet 将序列化的对象提交给 servlet,servlet 再将其传递给 backing bean,然后将失败或成功消息返回给 applet。如果提交成功,则小程序调用网页上的 javascript 函数来加载结果页面。这可确保保留正确的 backing bean。

最终代码如下:

小程序“提交搜索条件”按钮代码:

private void jButton8ActionPerformed(java.awt.event.ActionEvent evt)                                         
{                                             
    criteriaModel.loadCodeBase();
    int choice = JOptionPane.showConfirmDialog(this,
         "Are you sure you want to submit your search criteria and exit the \"Customise Search Criteria\" web page?",
         "Confirm Submit",
         JOptionPane.YES_NO_OPTION,
         JOptionPane.QUESTION_MESSAGE);

     if (choice == 0)
     {
         try 
         {      
             URL url = new URL(criteriaModel.getCodeBase(), "CriteriaServlet");

             System.out.println("Servlet address is: " + url);

            Object searchSubmitObject = criteriaModel.getObjectSlideData();

            HttpURLConnection connection = (HttpURLConnection)url.openConnection();
            connection.setDoInput(true);
            connection.setDoOutput(true);
            connection.setUseCaches(false);
            connection.setRequestMethod("POST");
            connection.setRequestProperty("Content-Type","application/x-java-serialized-object");

            ObjectOutputStream out = new ObjectOutputStream(connection.getOutputStream());
            out.writeObject(searchSubmitObject);
            out.flush();
            out.close();
            out.close();

            System.out.println("Object Written");

            ObjectInputStream in = new ObjectInputStream(connection.getInputStream());
            String response = (String)in.readObject();
            System.out.println(response);
            in.close();

            if(response.equals("Failed"))
            {
                JOptionPane.showMessageDialog(jPanel8, "Submit Search criteria file to server failed.\n Try Again later.");
            }
            else
            {
                getAppletContext().showDocument(new URL("javascript:openResultsPage()"));
            }
         } 
         catch (MalformedURLException ex)
        {
            JOptionPane.showMessageDialog(jPanel8, "Submit criteria file Malformed URL."
                    + ex.toString());
            System.out.println("MalformedURLException occurred");
            Logger.getLogger(CriteriaInterfaceView.class.getName()).log(Level.SEVERE, null, ex);
        }
        catch (Exception e) 
         {
             System.out.println("Submit criteria file ERROR exception: " + e.toString());
             JOptionPane.showMessageDialog(jPanel8, "Submit criteria file ERROR exception:"
                    + e.toString());
         }
     }         
}      

在小服务程序中:

@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
{
    System.out.println("service(ServletRequest req, ServletResponse res)");

    res.setContentType("text/plain");

    try
    {
        ObjectInputStream in = new ObjectInputStream(req.getInputStream());
        slideData = (MultipleSlideDataObject2)in.readObject();
        in.close();

        String reply = "Failed";

        if(slideData != null)
        {
            System.out.println("Serial number of submitted slide series is: " + slideData.getSerialNumber());

            FacesContext facesContext = FacesUtil.getFacesContext(req, res);
            ProductSelection productSelection = (ProductSelection) facesContext.getApplication().evaluateExpressionGet(facesContext, "#{productSelection}", ProductSelection.class);
            productSelection.submitSearchCriteriaFile(slideData);

            reply = "Success";
        }

        ObjectOutputStream outputToApplet = new ObjectOutputStream(res.getOutputStream());
        outputToApplet.writeObject(reply);
        outputToApplet.flush();          
        outputToApplet.close();
    }
    catch (ClassNotFoundException ex)
    {
        Logger.getLogger(CriteriaServlet.class.getName()).log(Level.SEVERE, null, ex);
    }
}

在支持 bean 中:

public String submitSearchCriteriaFile(MultipleSlideDataObject2 slideData) throws IOException
{
    System.out.println("Recieved slide series with serial number: " + slideData.getSerialNumber());

    // If there is no slide data then...
    if (slideData == null)
    {
        return "process_MainSearchResultFailed";
    }
    else
    {
        rankProducts(slideData);
    }

    return "process_MainSearchResult";
}

在 JSF 页面的标题中,其中包含小程序:

<SCRIPT language="javascript">

    function openResultsPage()
    {
        window.location = "MainSearchResult.xhtml";
    }

    </SCRIPT>

FacesUtil 基于 BalusC 的 FacesUtil 类(对请求和响应类型的一些小改动)用于获取 servlet 中的支持 bean:

    package searchselection;

import javax.faces.FactoryFinder;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.context.FacesContextFactory;
import javax.faces.lifecycle.Lifecycle;
import javax.faces.lifecycle.LifecycleFactory;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class FacesUtil
{
    // Getters -----------------------------------------------------------------------------------

    public static FacesContext getFacesContext(
        ServletRequest request, ServletResponse response)
    {
        // Get current FacesContext.
        FacesContext facesContext = FacesContext.getCurrentInstance();

        // Check current FacesContext.
        if (facesContext == null) {

            // Create new Lifecycle.
            LifecycleFactory lifecycleFactory = (LifecycleFactory)
                FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY); 
            Lifecycle lifecycle = lifecycleFactory.getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);

            // Create new FacesContext.
            FacesContextFactory contextFactory  = (FacesContextFactory)
                FactoryFinder.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
            facesContext = contextFactory.getFacesContext(
                request.getServletContext(), request, response, lifecycle);

            // Create new View.
            UIViewRoot view = facesContext.getApplication().getViewHandler().createView(
                facesContext, "");
            facesContext.setViewRoot(view);                

            // Set current FacesContext.
            FacesContextWrapper.setCurrentInstance(facesContext);
        }

        return facesContext;
    }

    // Helpers -----------------------------------------------------------------------------------

    // Wrap the protected FacesContext.setCurrentInstance() in a inner class.
    private static abstract class FacesContextWrapper extends FacesContext {
        protected static void setCurrentInstance(FacesContext facesContext) {
            FacesContext.setCurrentInstance(facesContext);
        }
    } 

}
于 2012-09-04T16:31:26.103 回答