[saiku] 访问saiku首页的时候前后台处理流程
这篇文章讲述:
项目启动后,首次访问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));