OBIEE Web Service API 中的 HtmlViewService 未呈现报告(仅显示旋转加载器)

Posted

技术标签:

【中文标题】OBIEE Web Service API 中的 HtmlViewService 未呈现报告(仅显示旋转加载器)【英文标题】:HtmlViewService in OBIEE Web Service API is not rendering report (it's only showing spinning loader) 【发布时间】:2014-10-04 00:18:56 【问题描述】:

我有一个 Java 应用程序,它利用 OBIEE Web 服务 API 来使用来自 BI 服务器的数据。我能够很好地使用 XMLViewService 和 WebCatalogService,但我不能完全让 htmlViewService 在 Java 应用程序中正确呈现报告。该报告仅显示旋转加载器,但从未实际呈现报告。我很确定这与 Java 应用程序和 BI 服务器位于不同域的事实有关。 API 文档是这样说的:

在 Oracle BI Web 服务和第三方 Web 服务器不属于同一个域名服务 (DNS) 域的情况下,用户可能会收到与跨域脚本的浏览器安全约束相关的 JavaScript 错误。要避免这些问题,请使用 setBridge() 方法修改回调 URL 以指向第三方 Web 服务器。请注意,不提供由第三方 Web 服务器执行以将请求重新路由到 Oracle BI Web 服务的 Web 组件。此功能需要由第三方应用程序来实现。

几年前,由于 .NET 应用程序和 BI 服务器位于不同的域中,我使用 .NET/C# 进行了相同类型的集成并遇到了相同的问题。结果,我不得不创建一个 HTTP 处理程序(.ashx 文件)并使用 setBridge() 方法来解决问题。

我面临的挑战是找不到 Java 的 servlet 桥接示例。而且我对将.NET/.ASHX 代码移植到Java servlet/bridge 并不太有信心。有没有人可以提供任何代码示例或方向来为我指明正确的方向?这是一段代码,向您展示我正在做什么来拉回报告数据:

    // define report path
    ReportRef reportRef = new ReportRef();
    reportRef.setReportPath(reportFolder + "/" + reportName);

    // set page params
    StartPageParams pageParams = new StartPageParams(); 
    pageParams.setDontUseHttpCookies(true);

    // set report params
    String pageId = htmlService.startPage(pageParams, sawSessionId);
    String reportId = pageId + reportName;
    htmlService.addReportToPage(pageId, reportId, reportRef, null, null, null, sawSessionId);

    // get report html
    StringBuffer reportHtml = new StringBuffer(); 
    reportHtml.append(htmlService.getHtmlForReport(pageId, reportId, sawSessionId)); 

    // return html
    return reportHtml.toString();

这是浏览器中返回的错误:

XMLHttpRequest 无法加载 http://myobiserver.com/analytics/saw.dll?ajaxGo。请求的资源上不存在“Access-Control-Allow-Origin”标头。因此,Origin 'http://myjavaapp.com' 不允许访问。

根据要求,这是我的 .NET/.ASHX 桥:

using System.Collections.Specialized;
using System.Net;
using System.Text;
using System.Web;
using System;
using System.Collections;
using System.Configuration;
using System.Collections.Specialized;
using System.Web;
using System.Text;
using System.Net;
using System.IO;
using System.Diagnostics;

/*
This is a ASP.NET handler that handles communication 
between the SharePoint site and OracleBI.
It will be deployed to: 
C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\LAYOUTS\OracleBI
*/

public class OracleBridge: IHttpHandler

  public   bool IsReusable getreturn true;
  public OracleBridge()
  
  
  string getServer()
  
      string strServer = "http://<enter-domain>/analytics/saw.dll";
     int index = strServer.LastIndexOf("/");//split off saw.dll
     if (index >=0)
        return strServer.Substring(0,index+1);
     else
        return strServer;
  
  public void ProcessRequest(HttpContext context)
  
     HttpWebRequest req = forwardRequest(context);
     forwardResponse(context,req);
  
  private HttpWebRequest forwardRequest(HttpContext context)
  
     string strURL = makeURL(context);
     HttpWebRequest req = (HttpWebRequest)WebRequest.Create(strURL);
     req.Method = context.Request.HttpMethod;
     NameValueCollection   headers = context.Request.Headers;
     req.Accept  = headers.Get("Accept");
     req.Expect = headers.Get("Expect");
     req.ContentType = headers.Get("Content-Type");
     string strModifiedSince = headers.Get("If-Modified-Since");
     if (strModifiedSince != null && strModifiedSince.Length != 0)
        req.IfModifiedSince =  DateTime.Parse(strModifiedSince);
     req.Referer = headers.Get("Referer");
     req.UserAgent = headers.Get("User-Agent");    
     if (!req.Method.Equals("GET"))
     
        CopyStreams(context.Request.InputStream,req.GetRequestStream());
     
     return req;
  

  private void forwardResponse(HttpContext context, HttpWebRequest req)
  
     HttpWebResponse resp =null;
     try
     
        resp = (HttpWebResponse)req.GetResponse();
     
     catch(WebException e)
     
        resp =  (HttpWebResponse)e.Response;
     
     context.Response.StatusCode = (int)resp.StatusCode;

     for (int i = 0; i < resp.Cookies.Count; i++)
     
         Cookie c = resp.Cookies[i];
         HttpCookie hc = new HttpCookie(c.Name, c.Value);
         hc.Path = c.Path;
         hc.Domain = getServer();
         context.Response.Cookies.Add(hc);
     
     context.Response.ContentType = resp.ContentType;
     CopyStreams(resp.GetResponseStream(), context.Response.OutputStream);
  


  private string makeURL(HttpContext context)
  
     string strQuery = context.Request.Url.Query;
     string[] arrParams = strQuery.Split('?','&');
     StringBuilder resultingParams = new StringBuilder();
     string strURL=null;
     foreach(string strParam in arrParams )
      
        string[] arrNameValue = strParam.Split('=');
        if (!arrNameValue[0].Equals("RedirectURL"))
        
           if (strParam.Length != 0)
           
              if (resultingParams.Length != 0)
                 resultingParams.Append("&");
              resultingParams.Append(strParam);
           
        
        else if (arrNameValue.Length >1)
           strURL = HttpUtility.UrlDecode(arrNameValue[1]);
     

     if (strURL ==null)
        throw new Exception("Invalid URL format. requestURL parameter is missing");
     String sAppendChar = strURL.Contains("?") ? "&" : "?";
     if (strURL.StartsWith("http:") || strURL.StartsWith("https:"))
     
         String tmpURL = strURL + sAppendChar + resultingParams.ToString();
         return tmpURL;
     
     else
     
         String tmpURL = getServer() + strURL + sAppendChar + resultingParams.ToString();
         return tmpURL;
     
  

  private void CopyStreams(Stream inStr,Stream outStr)
  
     byte[] buf = new byte[4096];
     try
     
        do
        
           int iRead = inStr.Read(buf,0,4096);
           if (iRead == 0)
              break;
           outStr.Write(buf,0,iRead);
        
        while (true);
     
     finally
     
        outStr.Close();
     
 

【问题讨论】:

【参考方案1】:

使用先前答案中发布的链接 (http://pastebin.com/NibVnBLb) 中的 BridgeServlet 对我们不起作用。在我们的门户网站中,使用上面的 BridgeServlet 嵌入 Oracle BI 仪表板,将我们重定向到 OBI 登录页面,并且控制台日志显示不正确的资源 (js/css) Web 链接(本地 URL 而不是 OBIEE URL)。

相反,我们使用了这个类(稍作调整)https://gist.github.com/rafaeltuelho/9376341#file-obieehttpservletbridge-java。

使用 Java 11、Oracle Business Intelligence 12.2.1.4.0、OBIEE (http://OBIEE-server:port/analytics/saw.dll/wsdl/v12) 的 WSDL v12 测试,禁用 SSO。

这里是 BridgeServlet 类:

package com.abs.bi;

import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class OBIEEBridge
 */
public class BridgeServlet extends HttpServlet 

    private static final long serialVersionUID = 1L;

    /**
     * @see HttpServlet#HttpServlet()
     */
    public BridgeServlet() 
        super();
    

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
     *      response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException 

        try 
            this.processRequest(request, response);
         catch (Exception e) 
            throw new ServletException(e);
        
    

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
     *      response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException 

        try 
            this.processRequest(request, response);
         catch (Exception e) 
            throw new ServletException(e);
        
    

    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException 

        HttpURLConnection urlCon = forwardRequest(request);
        forwardResponse(response, urlCon);
    

    @SuppressWarnings("unchecked")
    private String decodeURL(HttpServletRequest request) 

        StringBuffer bufURL = new StringBuffer("");

        Map<String, String[]> params = request.getParameterMap();

        String[] arrURL = params.get("RedirectURL");
        String strURL = arrURL == null || arrURL.length == 0 ? null : arrURL[0];
        bufURL.append(strURL);

        int nQIndex = strURL.lastIndexOf('?');

        if (params != null && !params.isEmpty()) 
            bufURL.append(((nQIndex >= 0) ? "&" : "?"));
            Set<String> keys = params.keySet();
            Iterator<String> it = keys.iterator();
            while (it.hasNext()) 
                try 
                    String strKey = it.next();

                    if (strKey.equalsIgnoreCase("RedirectURL")) 
                        continue;
                    

                    String strEncodedKey = URLEncoder.encode(strKey, "UTF-8");

                    String[] paramValues = params.get(strKey);
                    for (String paramValue : paramValues) 
                        bufURL.append(strEncodedKey);
                        bufURL.append("=");
                        bufURL.append(URLEncoder.encode(paramValue, "UTF-8"));
                        bufURL.append("&");
                    
                 catch (UnsupportedEncodingException e) 
                    e.printStackTrace();
                
            

            bufURL.deleteCharAt(bufURL.length() - 1);
        

        return bufURL.toString();
    

    @SuppressWarnings("unchecked")
    private HttpURLConnection forwardRequest(HttpServletRequest request) throws IOException 

        String strURL = decodeURL(request);

        String[] arrURL = strURL.split("&", 2);
        String baseURL = arrURL[0];

        URL url = new URL(baseURL);
        HttpURLConnection con = (HttpURLConnection) url.openConnection();

        String strMethod = request.getMethod();
        con.setRequestMethod(strMethod);

        Enumeration<String> en = request.getHeaderNames();
        String strHeader;
        while (en.hasMoreElements()) 
            strHeader = en.nextElement();

            String strHeaderValue = request.getHeader(strHeader);
            con.setRequestProperty(strHeader, strHeaderValue);
        

        // is not a HTTP GET request
        if (strMethod.compareTo("GET") != 0) 

            con.setDoOutput(true);
            con.setDoInput(true);
            con.setUseCaches(false);

            DataOutputStream forwardStream = new DataOutputStream(con.getOutputStream());

            try 
                String urlParameters = arrURL[1];
                forwardStream.writeBytes(urlParameters);
                forwardStream.flush();
             finally 
                forwardStream.close();
            
        

        return con;
    

    private void forwardResponse(HttpServletResponse response, HttpURLConnection con) throws IOException 

        int nContentLen = -1;
        String strKey;
        String strValue;

        try 

            response.setStatus(con.getResponseCode());

            for (int i = 1; true; ++i) 
                strKey = con.getHeaderFieldKey(i);
                strValue = con.getHeaderField(i);

                if (strKey == null) 
                    break;
                

                if (strKey.equals("Content-Length")) 
                    nContentLen = Integer.parseInt(con.getHeaderField(i));
                    continue;
                

                if (strKey.equalsIgnoreCase("Connection") || strKey.equalsIgnoreCase("Server")
                        || strKey.equalsIgnoreCase("Transfer-Encoding") || strKey.equalsIgnoreCase("Content-Length")) 
                    continue; // skip certain headers
                

                if (strKey.equalsIgnoreCase("Set-Cookie")) 
                    String[] cookieStr1 = strValue.split(";");
                    String[] cookieStr2 = cookieStr1[0].split("=");
//                  String[] cookieStr3 = cookieStr1[1].split("=");

                    /*
                     * Change the Set-Cookie HTTP Header to remove the 'path' attribute. Thus the
                     * browser can accept the ORA_BIPS_NQID cookie from Oracle BI Server
                     */

                    Cookie c = new Cookie(cookieStr2[0], cookieStr2[1]);
                    c.setPath("/");
                    response.addCookie(c);

                 else 
                    response.setHeader(strKey, strValue);

                

            

            copyStreams(con.getInputStream(), response.getOutputStream(), nContentLen);

         finally 
            response.getOutputStream().close();
            con.getInputStream().close();
        
    

    private void copyStreams(InputStream inputStream, OutputStream forwardStream, int nContentLen) throws IOException 

        byte[] buf = new byte[1024];
        int nCount = 0;
        int nBytesToRead = 1024;
        int nTotalCount = 0;

        do 
            if (nContentLen != -1)
                nBytesToRead = nContentLen - nTotalCount > 1024 ? 1024 : nContentLen - nTotalCount;

            if (nBytesToRead == 0)
                break;

            // try to read some bytes from src stream
            nCount = inputStream.read(buf, 0, nBytesToRead);

            if (nCount < 0)
                break;

            nTotalCount += nCount;

            // try to write some bytes in target stream
            forwardStream.write(buf, 0, nCount);

         while (true);
    


AbsServiceUtils,其中包含对 Oracle BI 服务器的 SAWSessionService 和 HtmlViewService Web 服务调用:

package com.abs.bi;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspWriter;

import oracle.bi.web.soap.AuthResult;
import oracle.bi.web.soap.HtmlViewService;
import oracle.bi.web.soap.HtmlViewServiceSoap;
import oracle.bi.web.soap.ReportHTMLLinksMode;
import oracle.bi.web.soap.ReportHTMLOptions;
import oracle.bi.web.soap.ReportRef;
import oracle.bi.web.soap.SAWLocale;
import oracle.bi.web.soap.SAWSessionParameters;
import oracle.bi.web.soap.SAWSessionService;
import oracle.bi.web.soap.SAWSessionServiceSoap;
import oracle.bi.web.soap.StartPageParams;

public class AbsServiceUtils 

    private AbsServiceUtils() 
    

    public static URL buildWsdlUrl() throws MalformedURLException 
        return new URL(IAbsService.BASEWSDLURL);
    

    public static void writeBiContent(HttpServletRequest request, JspWriter out, String biReport) throws IOException 
        String userAgent = request.getHeader("User-Agent");
        Locale userLocale = request.getLocale();
        String bridgeServletContextPath = request.getContextPath() + "/bridgeservlet";
        String reportHtml = writeBiContent(biReport, userAgent, userLocale, bridgeServletContextPath);
        if (out != null) 
            out.println(reportHtml);
        
    

    public static String writeBiContent(String biReport, String userAgent, Locale userLocale,
            String bridgeServletContextPath) throws MalformedURLException 
        HtmlViewService htmlViewService = new HtmlViewService(buildWsdlUrl());
        HtmlViewServiceSoap htmlClient = htmlViewService.getHtmlViewService();

        SAWSessionService sAWSessionService = new SAWSessionService(buildWsdlUrl());
        SAWSessionServiceSoap myPort = sAWSessionService.getSAWSessionServiceSoap();

        SAWSessionParameters sessionparams = new SAWSessionParameters();
        sessionparams.setUserAgent(userAgent);
        SAWLocale sawlocale = new SAWLocale();
        sawlocale.setLanguage(userLocale.getLanguage());
        sawlocale.setCountry(userLocale.getCountry());
        sessionparams.setLocale(sawlocale);
        sessionparams.setAsyncLogon(false);

        AuthResult result = myPort.logonex(IAbsService.BIUSERNAME, IAbsService.BIPASSWORD, sessionparams);
        String sessionID = result.getSessionID();
        List<String> keepAliveSessionList = new ArrayList<>(1);
        keepAliveSessionList.add(sessionID);
        myPort.keepAlive(keepAliveSessionList);

        StartPageParams spparams = new StartPageParams();
        spparams.setDontUseHttpCookies(true);
        String pageID = htmlClient.startPage(spparams, sessionID);

        /**
         * This method will set the path to the servlet which will act like a bridge to
         * retrieve all the OBIEE resources like the javascript, CSS and the report.
         */
        if (bridgeServletContextPath != null) 
            htmlClient.setBridge(bridgeServletContextPath, sessionID);
        

        ReportHTMLOptions htmlOptions = new ReportHTMLOptions();
        htmlOptions.setEnableDelayLoading(false);
        htmlOptions.setLinkMode(ReportHTMLLinksMode.IN_PLACE.value());

        ReportRef reportref = new ReportRef();
        reportref.setReportPath(IAbsService.BIROOTPATH + biReport);

        StartPageParams startpageparams = new StartPageParams();
        startpageparams.setDontUseHttpCookies(false);

        htmlClient.addReportToPage(pageID, biReport.replace(" ", ""), reportref, null, null, htmlOptions, sessionID);

        String reportHtml = htmlClient.getHeadersHtml(pageID, sessionID);
        reportHtml = reportHtml + htmlClient.getHtmlForReport(pageID, biReport.replace(" ", ""), sessionID);
        reportHtml = reportHtml + htmlClient.getCommonBodyHtml(pageID, sessionID);

        return reportHtml;
    


IAbsService

package com.abs.bi;

public interface IAbsService 
    public static final String BASEWSDLURL = "http://<OracleBIServer:port>/analytics/saw.dll/wsdl/v12";
    public static final String BIUSERNAME = "USER";
    public static final String BIPASSWORD = "PASS";
    public static final String BIROOTPATH = "/shared/sharedfolder/";
    public static final String BIREPORTNAME = "report";


【讨论】:

【参考方案2】:

使用链接http://pastebin.com/NibVnBLb 查看java 的桥接代码。希望这可能会有所帮助。

【讨论】:

我遇到了三个不允许我构建的问题,我无法找到解决方法:1) "HttpProxyServlet.getHeaders(request, hashtable);"正在抛出“无法解决” 2)“URLConnection httpConnection = urlconn.openConnection();”正在抛出“类型 URL 未定义” 2) “com.abs.util.GetPost.rewriteHeaders(httpConnection, response);”正在抛出“无法解决”...最后,我在哪里插入 BI 服务器的基本 URL 的 URL? (在原始 C# 代码中是 "string strServer = "http:///analytics/saw.dll";")?谢谢! 我通过使用来自 java.net 的 URLConnection 找出了上面的项目符号 #2。仍在处理 #1 和 #3。 我错过了与此代码相关的另一个链接。查看此链接 pastebin.com/ndVt3HBu 以获取利用服务的代码。我希望不需要在桥代码中提供 BI 服务器的基本 url。如果我能找到 #1 问题的解决方案,我会更新你。 我对上一条评论感到抱歉。这是我的问题,我分配的用户代理是错误的。无论如何,即使在发送了正确的用户代理之后,最终也会调用 http:///analytics/saw.dll?getReportXmlFromSearchID ,然后是 localhost:57169/... 服务器 IP>%3a%2fanalytics% 2fsaw.dll%2fpages/common/ajaxloggedin.html 被调用。登录屏幕出现了。不知道为什么会发生这个问题。如果你能帮助我,那就太好了。提前致谢。 我遇到了同样的问题,因为正在调用 ajaxloggedin.html。不知道为什么。但是你能看看我上面的评论以及我在#1 和#3 中遇到的问题吗?我想知道这个问题是否与桥有关。正如我所说,我不得不在桥中注释掉几行代码,这可能是导致问题的原因。如果你弄清楚了,请告诉我!你能把更新的桥 servlet 代码发给我吗?谢谢。

以上是关于OBIEE Web Service API 中的 HtmlViewService 未呈现报告(仅显示旋转加载器)的主要内容,如果未能解决你的问题,请参考以下文章

Service Fabric 中的 Web API 损坏

读取 OBIEE 实例配置文件

如何在 Azure Service Fabric 中的自托管 Web API 上配置 SSL

从 Azure Service Fabric 中的不同应用程序与无状态 Web Api 服务通信

在 Service Fabric 上托管 Web API

将 OBIEE 与 Web 应用程序集成