2013-08-19 1 views
1

Мы использовали Java 5, Tomcat 5, Xalan и JSF 1 для создания приложения, использующего XSLT, XML и фильтр Tomcat, чтобы пользователи могли экспортировать свои данные в формате Excel. Недавно мы обновили Java 1.7.0_07, Tomcat 7.022 и JSF 2.1 (jsf-api-2.1.0-b03.jar). В связи с прилагаемыми усилиями мы еще не дошли до фишек; мы все еще используем jsp. Мы используем тег для отображения отчета Excel в своем всплывающем окне. Проблема в том, что после обновления всплывающее окно теперь отображает raw xml в IE, а не всплывающее открытие в Excel напрямую. Необработанный XML-файл можно сохранить из браузера в файл, и если этот сохраненный файл будет дважды щелкнут, он откроется в Excel правильно, но было бы лучше, если бы пользователи могли избежать этого.Экспорт в Excel с использованием JSF 2 и XSLT

Я считаю, что проблема заключается в том, что ответ в JSF 2 теперь совершается раньше, чем это было в JSF 1. Наш файл web.xml определяет следующие фильтры для Tomcat:

<filter> 
    <filter-name>XSLT Processor</filter-name> 
    <filter-class>com.cs.common.jsf.util.XsltProcessorFilter</filter-class> 
    </filter> 
    <filter-mapping> 
    <filter-name>XSLT Processor</filter-name> 
    <url-pattern>*.xml</url-pattern> 
    </filter-mapping> 

    <filter> 
    <filter-name>Hibernate Session Manager</filter-name> 
    <filter-class>com.cs.common.hibernate.HibernateSessionServletFilter</filter-class> 
    </filter> 
    <filter-mapping> 
    <filter-name>Hibernate Session Manager</filter-name> 
    <url-pattern>*.faces</url-pattern> 
    </filter-mapping> 
    <filter-mapping> 
    <filter-name>Hibernate Session Manager</filter-name> 
    <url-pattern>*.xml</url-pattern> 
    </filter-mapping> 

    <servlet> 
    <servlet-name>Faces Servlet</servlet-name> 
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> 
    <load-on-startup>0</load-on-startup> 
    </servlet> 
    <servlet-mapping> 
    <servlet-name>Faces Servlet</servlet-name> 
    <url-pattern>*.faces</url-pattern> 
    </servlet-mapping> 
    <servlet-mapping> 
    <servlet-name>Faces Servlet</servlet-name> 
    <url-pattern>*.xml</url-pattern> 
    </servlet-mapping> 

И наш XsltProcesserFilter класс содержит следующие строки:

fChain.doFilter(request, wrapper); 
response.setContentType("application/vnd.ms-excel"); 

используя sysouts, я решил, что contentType не будучи установлен под JSF 2, по-видимому, потому что ответ уже совершено. Я попытался установить contentType в jsp, который выводит xml, но JSF затем выдает много ошибок, поэтому, вероятно, мне нужно установить его позже в процессе (например, в вышеприведенном фильтре). Я пробовал response.setBufferSize(6400000) до doFilter в XsltProcessorFilter, так как я прочитал, что это может задержать фиксацию, но это также не решит проблему.

Как установить contentType в application/vnd.ms-excel после того, как лица завершили обработку, но до фиксации, чтобы браузер открылся в Excel?

+0

Отчет о проделанной работе - я загрузил исходный код JSF и поставил операторы sysout для локализации, где изменяется ContentType. Затем, перед изменением, я фиксирую существующее значение, а после изменения я его возвращаю. Теперь я получаю Excel, но форматирование неверно. Я планирую продолжить работу, пока не смогу опубликовать здесь ответ. –

ответ

1

Решение вышеуказанной проблемы связано с двумя проблемами. Первая проблема заключалась в том, что Tomcat очищал буфер и передавал ответ до возврата в XSLTProcessorFilter. Это было преодолено путем установки размера буфера на большое значение в XSLTProcessorFilter до передачи управления Faces. Затем класс Faces JspViewHandlingStrategy удалял вывод в двух точках. Это было преодолено добавлением атрибута запроса isExcelXML со значением «true» из XSLTProcessorFilter. Затем в JspViewHandlingStrategy было добавлено кодирование для проверки атрибута isXML, и если его значение было истинным, промывка была отключена. После этих изменений окно Excel теперь отображается пользователю с нужным форматированием. Конечно, можно просто прокомментировать два флеша в JspViewHandlingStrategy, но, по-видимому, они служат какой-то цели, поэтому решение здесь только обходит их, если они вызывают проблему (когда вызывается XSLTProcessorFilter).

Метод doFilter нашего XSLTProcessorFilter теперь содержит исправления:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain fChain) throws IOException, ServletException { 
     String xslTemplatePath = request.getParameter(XSLT_REQUEST_PARAM); 
     File xslTemplate = (xslTemplatePath == null) ? null : new File(servletPath, xslTemplatePath); 

     if ((xslTemplatePath != null) && xslTemplate.exists()) { 
      // Run the standard request processing to obtain the XML output 
      CharacterResponseWrapper wrapper = new CharacterResponseWrapper((HttpServletResponse) response); 
      response.setBufferSize(6400000); // This line overcomes Tomcat buffer flushing 
      request.setAttribute("isExcelXML", "true"); // This line signals to JSF to bypass the flushing 
      fChain.doFilter(request, wrapper); 
      response.setContentType("application/vnd.ms-excel"); 

// Transform the XML using the XSL stylesheet specified on the URL 
      Source xmlSource = new StreamSource(new StringReader(wrapper.toString())); 
      StreamSource xslSource = new StreamSource(xslTemplate); 

      try { 
       Transformer transformer = tFactory.newTransformer(xslSource); 
       StreamResult out = new StreamResult(response.getWriter());    
       transformer.transform(xmlSource, out); 

      } catch (Throwable t) { 
       t.printStackTrace(response.getWriter()); 
      } 

     } else { // standard processing 
      fChain.doFilter(request, response); 
     } 
    } 

Измененная часть класса JspViewHandlingStrategy находится в конце этого публичный недействительным RenderView (FacesContext контекст, UIViewRoot вид) метод:

//For XML output to Excel, bypass later flushings 
boolean bypassFlush = false; 
if (((ServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest()).getAttribute("isExcelXML")!=null 
&& ((ServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest()).getAttribute("isExcelXML").toString().equals("true")) { 
    bypassFlush = true; 
} 

    // write any AFTER_VIEW_CONTENT to the response (This comment in original JSF file) 
    // side effect: AFTER_VIEW_CONTENT removed (This comment in original JSF file) 
    ViewHandlerResponseWrapper wrapper = (ViewHandlerResponseWrapper) 
      RequestStateManager.remove(context, RequestStateManager.AFTER_VIEW_CONTENT); 
    if (null != wrapper && !bypassFlush) { //fix to Excel issue involved bypassing flush if isExcelXML set to true 
     wrapper.flushToWriter(extContext.getResponseOutputWriter(), 
           extContext.getResponseCharacterEncoding()); 
    } 

    if (!bypassFlush) { //fix to Excel issue involved bypassing flush if isExcelXML set to true 
     extContext.responseFlushBuffer(); 
} 

Поскольку для решения этой проблемы потребовалось несколько недель, возможно, стоит добавить некоторые подробности о том, как это было отлажено, если другие могут использовать эту технику.

Отладка началась с загрузки исходного кода JSF. Операторы system.out, println() помещались в каждый исходный код файла, начиная с FacesServlet.java в его методе обслуживания. Целью отладки было увидеть, где isCommitted boolean переключился с «false» на «true», поскольку именно это и вызывало проблему.Заявления, подобные следующие были использованы в течение каждого анализируемого файла исходного кода:

System.out.println("someClass someLineId - " + someObjectReference.getClass().getName() + 
    ((ServletResponse)FacesContext.getCurrentInstance().getExternalContext().getResponse()).isCommitted() + 
    ((ServletResponse)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getContentType()); 

(Обратите внимание, что «импорт javax.servlet.ServletResponse,» как правило, должны были быть добавлены операторы импорта в исходном файле.) После того, строка была найдена, где значение isCommitted было ложным перед этим и истинно после него, тогда отладочное усилие переключилось на экземпляр класса, который был вызван этой линией. Этот процесс был продолжен до тех пор, пока не будут обнаружены проблемные линии смыва буфера.

Конечно, описанные изменения должны были быть запущены из проекта, чтобы найти проблему. Каждый исходный файл должен быть скомпилирован (с пулом классов, содержащим множество банок конечного проекта). После того, как класс был скомпилирован, оригинальная грань была переименована в zip-файл, а вновь скомпилированный класс помещен в zip-файл, перезаписав версию этого файла, которая уже была там. Затем zip-файл был переименован в банку. Затем банку помещали в Eclipse, проект перекомпилировали, и проект был запущен. Выход был обнаружен в окне вывода Tomcat. Когда несколько классов были результатом одной компиляции (как это может происходить с внутренними классами), все новые скомпилированные классы были помещены на их место. (Хотя это, возможно, и не было.)

Смежные вопросы