Сегодня расскажу про решение проблемы с кириллическими названиями отчетов в 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 - в итоге любимый продукт будет улучшен, но ничего с собой поделать не могу ;)