這篇文章講述:
項目啟動后,首次訪問SAIKU的登錄頁,前后台分別做了什么處理
(1) 訪問的到底是什么頁面?
瀏覽器輸入:localhost:8080 啪一回車 根據web訪問的尿性,訪問的是 index.jsp 或者 index.html 先看看 index.jsp ,沒什么內容 <%@ taglib prefix="authz" uri="http://www.springframework.org/security/tags" %> <%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <title>Pentaho BI Platform</title> <META HTTP-EQUIV="refresh" CONTENT="0;URL=./serverdocs"> </head> <body> </body> </html> <meta http-equiv="refresh" content="0; url="> 表示:經過一段時間轉到另外某個頁面 這里0表示沒有延時,直接跳轉到后面的URL;把0改成1,則延時1秒后跳轉。 這就是為什么有的人訪問localhost:8080會跳轉到localhost:8080/saiku/serverdocs的原因 再看看 index.html 代碼太多,你需要知道一點:配置中引入了所有需要的JS和CSS(除了一些需要動態加載的JS和CSS) 因為不管訪問什么請求,地址欄都是localhost:8080沒變過,得知所有的接口調用都是ajax異步請求 因此,所有需要的JS和CSS必然都是在index.html中引入包含了 目前迫切關注這幾個引入: <script src="js/jquery/jquery.min.js" type="text/javascript"></script>//基本JS <script type="text/javascript" src="js/backbone/backbone.js"></script>//框架JS //Saiku 重要的JS <script type="text/javascript" src="js/saiku/Settings.js"></script>//saiku基本配置->源碼 <script type="text/javascript" src="js/saiku/Saiku.js"></script>//初始化saiku基本信息 <script type="text/javascript" src="js/saiku/models/Plugin.js"></script>//訪問接口動態獲取JS插件 <script type="text/javascript" src="js/saiku/models/Settings.js"></script>//訪問接口動態獲取CSS插件 <script type="text/javascript" src="js/saiku/adapters/SaikuServer.js"></script>//URL請求交互處理 <script type="text/javascript" src="js/saiku/models/Session.js"></script>//用戶狀態信息操作類 <script type="text/javascript" src="js/saiku/views/LoginForm.js"></script>//創建用戶登錄窗口 結論:index.html
(2) 首頁,你在初始化自己的時候,都干哈了好事?
Saiku.js 跳了出來 ,他說:在全部的js中,哥們兒我有唯一的一個 $(function(){ //頁面初始化加載代碼於此 }); 我在這里面做了以下的好事,我按順序說了啊 1、先判斷是不是作為BI的PLUGIN存在,如果不是,那我可是一個獨立的系統,是需要登錄的喲,是就算了撒 2、顯然我是被獨立了,那我就開始初始化加載吧 3、我第一件事就是要構造一個獲取動態JS的接口 URL :/saiku/rest/saiku/info 4、接下來當然是要訪問這個接口啦 |——失敗后我不做任何處理,反正我破罐子破摔了 |——成功后嘿嘿【接口處理代碼和返回的JS列表看最后】 5、我再去構造一個獲取動態CSS的接口 URL:/saiku/rest/saiku/info/ui-settings 6、接下來當然還是要訪問這個接口啦 |——這么說吧,我如果成功success了,我要做以下的事: |——加載所有的JS[plugins.size次異步請求] |——加載所有的CSS[css.size次異步請求] |——如果我不小心失敗error |——我就只能加載所有的JS[plugins.size次異步請求]啦,你總不能要求我加載獲取失敗的CSS吧 |——可是不管我是成功還是失敗,我要做的事就必須做完,這些事我必做的: 1)創建Session對象 Saiku.session = new Session({}, {username: Settings.USERNAME,password: Settings.PASSWORD}); 在創建的時候會調用check_session方法通過構造/saiku/rest/saiku/session並訪問該接口去獲取用戶session信息 如果用戶session信息為空就創建new LoginForm()登錄頁 否則調用load_session方法構建首頁空間 - new SessionWorkspace() |-其實是訪問/saiku/rest/saiku/用戶帳號/discover接口獲取數據源,成功后返回的connectionsList |-成功后調用process_datasources方法 傳入返回的 connectionsList 作為參數 |- 根據 connectionsList 執行 prefetch_dimensions 預加載Cube -> _.delay(this.prefetch_dimensions, 20); 鑽取層級 var connection = this.connections[i]; var catalog = connection.catalogs[j]; var schema = catalog.schemas[k]; var cube = schema.cubes[l]; var key = connection.name + "/" + catalog.name + "/" + ((schema.name === "" || schema.name === null) ? "null" : schema.name) + "/" + encodeURIComponent(cube.name); this.cube[key] = new Cube({ key: key }); if (Settings.DIMENSION_PREFETCH === true) { this.cube[key].fetch(); } |- 最后新建Workspace或者直接打開查詢 -> Saiku.tabs.add(new Workspace()); 2)創建Toolbar對象 Saiku.toolbar = new Toolbar();
(3) 動態獲取JS
1、關鍵代碼 - InfoResource.getAvailablePlugins()
filepath配置在saiku-beans的這個bean里面
<bean id="platformBean" class="org.saiku.service.PlatformUtilsService">
<property name="path" value="../webapps/saiku/js/saiku/plugins/"/>
</bean>
2、動態加載的JS如下
(4) $(function(){}); 源碼詳細分析
if (! Settings.BIPLUGIN) {//Settings.BIPLUGIN 在settings.js中配置為false //執行頁面初始化加載 $(document).ready(function () { //這個JS對象在js/saiku/models/Plugin.js里面定義 //完成后構建了訪問的url:/saiku/rest/saiku/info /* js/saiku/models/Plugin.js var PluginCollection = Backbone.Collection.extend({ model: Plugin, url: 'info' }); */ var plugins = new PluginCollection(); //接着,執行 backbone 的 fetch 方法就去訪問了這個rest服務 /saiku/rest/saiku/info plugins.fetch({ //調用成功。返回各個模塊的plugin.js的路徑 success: function () { //這個JS對象在js/saiku/models/Settings.js里面定義 //完成后構建了訪問的url:/saiku/rest/saiku/info/ui-settings /* js/saiku/models/Settings.js var SettingsOverrideCollection = Backbone.Collection.extend({ model: SettingsOverride, url: 'info/ui-settings' }); */ var settingsoverride = new SettingsOverrideCollection(); //接着執行 backbone 的 fetch 方法就去訪問了這個rest服務 /saiku/rest/saiku/info/ui-settings settingsoverride.fetch({ //調用成功。返回一個全局css文件 //加載第一個接口返回的JS和第二個接口返回的CSS //並根據創建Session對象的狀態去決定到登錄頁還是首頁 //創建Toolbar工具欄 success: function () { var i = plugins.size(); var j = 0; //遍歷第一個rest返回的plugin.js,逐個去異步獲取 plugins.each(function (log) { j = j + 1; if (log.attributes.path != "js/saiku/plugins/I18n/plugin.js") { jQuery.ajax({ async:false, type:'GET', url:log.attributes.path, data:null, success:function(){ //JS全部遍歷完畢后,再遍歷第二個接口獲取到的CSS if (j == i) { var k = settingsoverride.size(); var l = 0; settingsoverride.each(function (log) { l = l + 1; for (var key in log.attributes) { Settings[key] = log.attributes[key]; } if(Settings.CSS != undefined){ Saiku.loadCSS(Settings.CSS, null) } //CSS全部遍歷完畢后,創建一個Session:js/saiku/models/Session.js //創建session會調用初始化方法,從而調用check_session方法 /** check_session: function() { if (this.sessionid === null || this.username === null || this.password === null) { var that = this; this.clear(); //在這里會調用本頁面的url 構造的接口是/saiki/rest/saiku/session //調用成功后調用process_session /** url: function() {return "session";} **/ this.fetch({ success: this.process_session, error: this.brute_force }); } else { this.username = encodeURIComponent(options.username); this.load_session(); } }, **/ /** process_session: function(model, response) { if ((response === null || response.sessionid == null)) { // Open form and retrieve credentials Saiku.ui.unblock(); if (Settings.DEMO) { this.form = new DemoLoginForm({ session: this }); } else { //第一次訪問肯定沒有用戶信息 //所以就創建一個LoginForm了 //至此就完成了首頁初次訪問登錄框的加載 this.form = new LoginForm({ session: this }); } this.form.render().open(); } else { this.sessionid = response.sessionid; this.roles = response.roles; this.isAdmin = response.isadmin; this.username = encodeURIComponent(response.username); this.language = response.language; if (typeof this.language != "undefined" && this.language != Saiku.i18n.locale) { Saiku.i18n.locale = this.language; Saiku.i18n.automatic_i18n(); } this.load_session(); } return this; }, **/ //再創建工具欄對象:js/saiku/views/Toolbar.js if (k == l) { Saiku.session = new Session({}, { username: Settings.USERNAME, password: Settings.PASSWORD }); Saiku.toolbar = new Toolbar(); } }); } }, dataType:'script' }); } else{ if (j == i) { var k = settingsoverride.size(); var l = 0; settingsoverride.each(function (log) { l = l + 1; for (var key in log.attributes) { Settings[key] = log.attributes[key]; } if(Settings.CSS != undefined){ Saiku.loadCSS(Settings.CSS, null) } if (k == l) { Saiku.session = new Session({}, { username: Settings.USERNAME, password: Settings.PASSWORD }); Saiku.toolbar = new Toolbar(); } }); } } }); }, //如果ui-settings訪問失敗,只加載第一個接口返回的JS //並根據創建Session對象的狀態去決定到登錄頁還是首頁 //創建Toolbar工具欄 error: function () { var i = plugins.size(); var j = 0; plugins.each(function (log) { j = j + 1; if (log.attributes.path != "js/saiku/plugins/I18n/plugin.js") { jQuery.ajax({ async:false, type:'GET', url:log.attributes.path, data:null, success: function(){ if (j == i) { if(Settings.CSS != undefined){ Saiku.loadCSS(Settings.CSS, null) } Saiku.session = new Session({}, { username: Settings.USERNAME, password: Settings.PASSWORD }); Saiku.toolbar = new Toolbar(); } }, dataType:'script' }); } else{ if (j == i) { if(Settings.CSS != undefined){ Saiku.loadCSS(Settings.CSS, null) } Saiku.session = new Session({}, { username: Settings.USERNAME, password: Settings.PASSWORD }); Saiku.toolbar = new Toolbar(); } } }); } }); } }); }); }
(5) Settings.js源碼
var Settings = { VERSION: "Saiku-${version}", BIPLUGIN: false, BIPLUGIN5: false, BASE_URL: window.location.origin, TOMCAT_WEBAPP: "/saiku", REST_MOUNT_POINT: "/rest/saiku/", DIMENSION_PREFETCH: true, DIMENSION_SHOW_ALL: true, DIMENSION_SHOW_REDUCED: false, ERROR_LOGGING: false, I18N_LOCALE: "en", // number of erroneous ajax calls in a row before UI cant recover ERROR_TOLERANCE: 3, QUERY_PROPERTIES: { 'saiku.olap.query.automatic_execution': true, 'saiku.olap.query.nonempty': true, 'saiku.olap.query.nonempty.rows': true, 'saiku.olap.query.nonempty.columns': true, 'saiku.ui.render.mode' : 'table', 'saiku.olap.query.filter' : true, 'saiku.olap.result.formatter' : "flattened" }, TABLE_LAZY: true, // Turn lazy loading off / on TABLE_LAZY_SIZE: 1000, // Initial number of items to be rendered TABLE_LAZY_LOAD_ITEMS: 20, // Additional item per scroll TABLE_LAZY_LOAD_TIME: 20, // throttling call of lazy loading items /* Valid values for CELLSET_FORMATTER: * 1) flattened * 2) flat */ CELLSET_FORMATTER: "flattened", // limits the number of rows in the result // 0 - no limit RESULT_LIMIT: 0, MEMBERS_FROM_RESULT: true, MEMBERS_LIMIT: 3000, MEMBERS_SEARCH_LIMIT: 75, ALLOW_IMPORT_EXPORT: false, ALLOW_PARAMETERS: true, PLUGINS: [ "Chart" ], DEFAULT_VIEW_STATE: 'view', // could be 'edit' as well DEMO: false, TELEMETRY_SERVER: 'http://telemetry.analytical-labs.com:7000', LOCALSTORAGE_EXPIRATION: 10 * 60 * 60 * 1000 /* 10 hours, in ms */, UPGRADE: true, EVALUATION_PANEL_LOGIN: true, QUERY_OVERWRITE_WARNING: true, MAPS: true, MAPS_TYPE: 'OSM' // OSM || GMAPS }; /** * Extend settings with query parameters */ Settings.GET = function () { var qs = document.location.search; qs = qs.split("+").join(" "); var params = {}, tokens, re = /[?&]?([^=]+)=([^&]*)/g; tokens = re.exec(qs); while (tokens) { var value = decodeURIComponent(tokens[2]); if (! isNaN(value)) value = parseInt(value); if (value === "true") value = true; if (value === "false") value = false; if(decodeURIComponent(tokens[1].toUpperCase()).substring(0,5)==="PARAM"){ params["PARAM"+decodeURIComponent(tokens[1]).substring(5,decodeURIComponent(tokens[1]).length)] = value; } else{ params[decodeURIComponent(tokens[1]).toUpperCase()] = value; } tokens = re.exec(qs); } return params; }(); _.extend(Settings, Settings.GET); Settings.PARAMS = (function() { var p = {}; for (var key in Settings) { if (key.match("^PARAM")=="PARAM") { p[key] = Settings[key]; } } return p; }()); Settings.REST_URL = Settings.TOMCAT_WEBAPP + Settings.REST_MOUNT_POINT; // lets assume we dont need a min width/height for table mode if (Settings.MODE == "table") { Settings.DIMENSION_PREFETCH = false; $('body, html').css('min-height',0); $('body, html').css('min-width',0); } if (Settings.BIPLUGIN5) { Settings.BIPLUGIN = true; } Settings.INITIAL_QUERY = false; if (document.location.hash) { var hash = document.location.hash; if (hash.length > 11 && hash.substring(1, 11) == "query/open") { Settings.INITIAL_QUERY = true; } } Settings.MONDRIAN_LOCALES = { "English": "en_US", "Dutch": "nl_BE", "French": "fr_FR" }; /** * < IE9 doesn't support Array.indexOf */ if (!Array.prototype.indexOf) { Array.prototype.indexOf = function(elt /*, from*/) { var len = this.length >>> 0; var from = Number(arguments[1]) || 0; from = (from < 0) ? Math.ceil(from) : Math.floor(from); if (from < 0) from += len; for (; from < len; from++) { if (from in this && this[from] === elt) return from; } return -1; }; } var tagsToReplace = { '&': '&', '<': '<', '>': '>' }; function replaceTag(tag) { return tagsToReplace[tag] || tag; } function safe_tags_replace(str) { return str.replace(/[&<>]/g, replaceTag); } if ($.blockUI) { $.blockUI.defaults.css = {}; $.blockUI.defaults.overlayCSS = {}; $.blockUI.defaults.blockMsgClass = 'processing'; $.blockUI.defaults.fadeOut = 0; $.blockUI.defaults.fadeIn = 0; $.blockUI.defaults.ignoreIfBlocked = false; } if (window.location.hostname && (window.location.hostname == "try.meteorite.bi" )) { Settings.USERNAME = "admin"; Settings.PASSWORD = "admin"; Settings.DEMO = true; Settings.UPGRADE = false; } var isIE = (function(){ var undef, v = 3; var dav = navigator.appVersion; if(dav.indexOf('MSIE') != -1) { v = parseFloat(dav.split('MSIE ')[1]); return v> 4 ? v : false; } return false; }()); var isFF = (function(userAgent) { 'use strict'; return !!userAgent.match(/Firefox/); }(navigator.userAgent)); var isMobile = (function(userAgent) { 'use strict'; return !!userAgent.match(/android|webos|ip(hone|ad|od)|opera (mini|mobi|tablet)|iemobile|windows.+(phone|touch)|mobile|fennec|kindle (Fire)|Silk|maemo|blackberry|playbook|bb10\; (touch|kbd)|Symbian(OS)|Ubuntu Touch/i); }(navigator.userAgent));