Сегодня расскажу про решение проблемы с кириллическими названиями отчетов в BI Publisher 11g.
Суть в том, что MS Internet Explorer некорректно отображает имена отчетов, чьи названия в кириллице.
Проблема возникает вследствие потери символа "%" где-то в недрах java-классов BIP для кодированной в UTF8 строки с названием файла результата отчета.
А значит задача нашего workaround в том, чтобы привести подобные "битые" строки к нормальному виду:
С помощью HTTP-сниффера видно, что испорчено значение content-disposition у HTTP-ответа.
Сам workaround заключается в следующем:
1) Создание нового фильтра сервлетов, который будет подменять логику метода setHeader для HttpServletResponse.
2) Сопоставление этого фильтра с xdo-сервлетами (отвечающие за вывод пользователю результатов отчетов).
Привожу java код для класса фильтра:
package oracle.xdo.servlet; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; public class XxCyrillicFilter implements Filter { protected FilterConfig config; public void init(FilterConfig config) throws ServletException { this.config = config; } public void destroy() { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { ServletResponse newResponse = response; if (request instanceof HttpServletRequest) { String uAgent = ((HttpServletRequest)request).getHeader("User-Agent"); boolean isMSIE = ( uAgent != null && uAgent.indexOf( "MSIE" ) != -1 ); if (isMSIE) newResponse = new XxResponseWrapper((HttpServletResponse) response); } chain.doFilter(request, newResponse); } } class XxResponseWrapper extends HttpServletResponseWrapper { private String cyr = new String("\u0430\u0431\u0432\u0433\u0434\u0435\u0451\u0436\u0437\u0438\u044B\u0439\u043A\u043B\u043C\u043D\u043E\u043F\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044C\u044D\u044E\u044F"); private String [] lat = {"a","b","v","g","d","e","yo","g","z","i","y","i", "k","l","m","n","o","p","r","s","t","u", "f","h","tz","ch","sh","sh","'","e","yu","ya"}; public String translitIt(String str) { str = str.toLowerCase(); StringBuffer newStr = new StringBuffer(""); char [] chs = str.toCharArray(); for (int i = 0; i < chs.length; i++) { int k = cyr.indexOf(chs[i]); if (k != -1) newStr.append(lat[k]); else newStr.append(chs[i]); } return newStr.toString(); } public XxResponseWrapper(HttpServletResponse response) { super(response); } public void setHeader(String name, String value) { if (!"content-disposition".equalsIgnoreCase(name)) super.setHeader(name, value); else { String changedCD = value; if (value != null) { int endIndexFirstPart = value.indexOf("filename=\"") + 10; int endIndexUnderscore = value.indexOf("_"); int endIndexDot = value.indexOf("."); int endIndex = endIndexUnderscore == -1 ? endIndexDot : endIndexUnderscore; String badString = value.substring(endIndexFirstPart, endIndex); StringBuffer sbuf = new StringBuffer(); for(int i = 0; i < badString.length(); i++) { char ch = badString.charAt(i); if(i % 2 == 0) sbuf.append("%"); sbuf.append(ch); } changedCD = value.substring(0, endIndexFirstPart) + sbuf.toString() + value.substring(endIndex); if (changedCD.length() > 150) try {changedCD = this.translitIt(URLDecoder.decode(changedCD, "UTF8"));} catch (UnsupportedEncodingException e) {} } super.setHeader("content-disposition", changedCD); } } }
Также выкладываю скомпилированные классы.
Из кода видно, что в переопределенном методе setHeader анализируется:
- является ли header нужным нам. т.е. "content-disposition";
- вызывается ли отчет из браузера IE.
Если все верно, то вычленяется "битая" строка, где перед каждой парой символов вставляется символ процента.
Затем полученная скорректированная строка заменяет битую часть заголовка "content-disposition".
И наконец проверяется длина полученной строки, если она велика ( > 150 ), то пытаемся перевести кириллические символы в транслит (тем самым избегаем проблемы открытия в MS Excel файло с ОЧЕНЬ длинным названием).
Кажется, самая сложная часть закончена...
Но нет!
Теперь нам следует указать веб-приложению xmlpserver использовать созданный фильтр сервлетов, а также сопоставить его с определенными сервлетами.
Тут важно знать, что правка файла web.xml (дескриптор веб-приложений) в stage-каталогах ни к чему не приведет!
Поэтому нам потребуется:
1) В каталоге с ear-архивами основных веб-приложений (..\Middleware\Oracle_BI1\bifoundation\jee )
создать бекап файла xmlpserver.ear, создать ПАПКУ с именем xmlpserver.ear, в которую разархивировать все содержимое исходного ear-архива.
2) Теперь, уже внутри каталога xmlpserver.ear найти архив xmlpserver.war, и в нем-то скорректировать файл
..\WEB-INF\web.xml
3) Добавить в web.xml нужно следующий код:
<filter> <filter-name>XxCyrillicFilter</filter-name> <filter-class>oracle.xdo.servlet.XxCyrillicFilter</filter-class> </filter> <filter-mapping> <filter-name>XxCyrillicFilter</filter-name> <url-pattern>/servlet/xdo</url-pattern> </filter-mapping> <filter-mapping> <filter-name>XxCyrillicFilter</filter-name> <url-pattern>*.xdo</url-pattern> </filter-mapping>
4) Также следует создать в папке WEB-INF каталог classes c java-классами данного решения (включая полный путь классов - WEB-INF/classes/oracle/xdo/servlet/...)
5) И наконец, следует обновить развернутое веб-приложение bipublisher через консоль Weblogic
Вот и все, теперь, после перезагрузки приложения bipublisher, отчеты в IE будут формироваться следующим образом:
Сергей,
ОтветитьУдалитьМощно, снимаю шляпу! А ответствующую багу на support.oracle.com завели ?
Роман, спасибо!
ОтветитьУдалитьЕсть у меня глупая привычка - находить workaround'ы вместо "плодотворной" работы с техподдержкой Oracle.
Умом понимаю, что заведи я SR - в итоге любимый продукт будет улучшен, но ничего с собой поделать не могу ;)