/**
       * @class ServerScript.app
       * 
       * ServerScript app是HttpServletRequest和HttpServletResponse关联的服务器端的JavaScript方法库。
       */
      app = {
        /**
         * 向当前用户的浏览器控制台中输出指定对象的日志信息。
         * @param {Object} object 打印的对象。
         */
        log: function(object) {
          Console.print(request, Wb.encode(object), 'log', true);
        },
        /**
         * 向当前用户的浏览器控制台中输出指定对象的提示信息。
         * @param {Object} object 打印的对象。
         */
        info: function(object) {
          Console.print(request, Wb.encode(object), 'info', true);
        },
        /**
         * 向当前用户的浏览器控制台中输出指定对象的警告信息。
         * @param {HttpServletRequest} request 请求对象。该请求对象用于关联到对应用户。
         * @param {Object} object 打印的对象。
         */
        warn: function(object) {
          Console.print(request, Wb.encode(object), 'warn', true);
        },
        /**
         * 向当前用户的浏览器控制台中输出指定对象的错误信息。
         * @param {Object} object 打印的对象。
         */
        error: function(object) {
          Console.print(request, Wb.encode(object), 'error', true);
        },
        /**
         * 向当前用户的客户端发送指定对象。
         * @param {Object} object 发送的对象。
         * @param {Boolean} [successful] 是否成功标识。如果指定该参数将采用JSON格式封装,该模式仅适用于文件上传模式的数据发送。
         */
        send: function(object, successful) {
          if (Wb.isObject(object) || Wb.isArray(object))
            object = Wb.encode(object);
          if (successful === undefined && WebUtil.jsonResponse(request)) {
            if (object === null)
              object = ''; //兼容java
            else if (object === undefined)
              object = 'undefined';
            else
              object = object.toString();
            successful = true; //upload模式的json格式响应
          }
          if (successful === undefined)
            WebUtil.send(response, object);
          else
            WebUtil.send(response, object, successful);
        },
        /**
         * 获取文件控件上传的单个或多个文件。
         * @param {String} fieldName 文件控件名称。
         * @return {Object[]} 获取的文件数据[{stream:输入流,name:文件名称,size:文件长度},...],如果未找到返回空数组。
         */
        getFiles: function(fieldName) {
          var files = [],
            index = '',
            prefix = '',
            data;

          function getData(name) {
            var file = request.getAttribute(name);
            if (file instanceof java.io.InputStream)
              return {
                stream: file,
                name: request.getAttribute(name + '__name'),
                size: request.getAttribute(name + '__size')
              };
            else
              return null;
          }
          while ((data = getData(fieldName + prefix + index))) {
            files.push(data);
            if (index === '') {
              index = 0;
              prefix = '@';
            }
            index++;
          }
          return files;
        },
        /**
         * 获取HttpServletRequest和HttpSession对象中指定名称的属性或参数。
         * 如果相同名称的属性或参数都存在则返回优先顺序最高的值。优先顺序依次为
         * HttpSession的attribute,HttpServletRequest的attribute和
         * HttpServletRequest的parameter。如果都不存在则返回null。
         * 如果name参数缺省,将返回所有值组成的对象。
         * @param {String} [name] 参数或属性或称。
         * @param {Boolean} [returnString] 如果获取单个参数,false返回原始对象值,true返回原始对象转换为字符串后的值。默认为false。
         * @return {Object} 请求对象的parameter和attribute中的值组成的对象。
         */
        get: function(name, returnString) {
          if (name === undefined) {
            //获取全部值
            var jo = WebUtil.fetch(request),
              result = {};
            jo.entrySet().forEach(function(e) {
              result[e.key] = e.value;
            });
            return result;
          } else {
            //获取单个值
            return returnString ? WebUtil.fetch(request, name) : WebUtil.fetchObject(request, name);
          }
        },
        /**
         * 获取指定名称的参数,并转换为布尔类型。
         * @param {String} name 参数名称。
         * @return {Boolean} 参数转换后的布尔值。
         */
        getBool: function(name) {
          return Wb.parseBool(app.get(name));
        },
        /**
         * 获取指定名称的参数,并转换为整数类型。
         * @param {String} name 参数名称。
         * @return {Integer} 参数转换后的整数值。
         */
        getInt: function(name) {
          return parseInt(app.get(name), 10);
        },
        /**
         * 获取指定名称的参数,并转换为浮点数类型。
         * @param {String} name 参数名称。
         * @return {Double} 参数转换后的浮点值。
         */
        getFloat: function(name) {
          return parseFloat(app.get(name));
        },
        /**
         * 获取指定名称的参数,并转换为日期类型。参数必须为有效日期格式的字符串。
         * @param {String} name 参数名称。
         * @return {Date} 参数转换后的日期值。
         */
        getDate: function(name) {
          return Wb.strToDate(app.get(name, true));
        },
        /**
         * 获取指定名称的参数,并转换为Java日期类型。参数必须为有效日期格式的字符串。
         * @param {String} name 参数名称。
         * @return {Date} 参数转换后的日期值。
         */
        getJavaDate: function(name) {
          return DateUtil.strToDate(app.get(name, true));
        },
        /**
         * 获取指定名称的参数,并转换为对象/数组。
         * @param {String} name 参数名称。
         * @return {Object/Array} 参数转换后的对象。
         */
        getObject: function(name) {
          var str = app.get(name, true);
          if (!str)
            throw 'Param "' + name + '" is null or blank';
          return Wb.decode(str);
        },
        /**
         * 获取指定名称的参数,并转换为Java字符串数组。
         * @param {String} name 参数名称。
         * @return {JavaString[]} 参数转换后的对象。
         */
        getJavaArray: function(name) {
          return Java.to(app.getObject(name), Java.type('java.lang.String[]'));
        },
        /**
         * 判断指定参数是否不为空。参数是指存储在session的attribute,request的attribute或parameter中的值。
         * @param {String} name 参数名称。
         * @return {Boolean} 如果参数存在且其值不为空则返回true,否则返回false。
         */
        has: function(name) {
          return !Wb.isEmpty(app.get(name, true));
        },
        /**
         * 遍历对象,并把对象中每个条目的值设置到request的attribute对象,attribute名称为对象中条目的名称。
         * 如果首个参数不是对象,将以第1个参数为名称,第2个参数为值,设置到attribute。
         * @param {Object/String/Map/JSONObject} object 设置的对象。
         * @param {Object} [val] 如果object为字符串,该项为设置的值。
         */
        set: function(object, val) {
          if (Wb.isObject(object) || ((SysUtil.isMap(object) || object instanceof JSONObject) && object.entrySet)) {
            Wb.each(object, function(k, v) {
              request.setAttribute(k, v);
            });
          } else if (object)
            request.setAttribute(object, val);
        },
        /**
         * 启动jndi指定的数据库连接的事务,如果指定的连接已经启动事务则该方法没有任何效果。
         * @param {String} [jndi] 数据库jndi变量名称。为空表示使用默认使用库。
         * @param {Integer} [isolation] 事务的孤立程度,值对应Connection.TRANSACTION_XXX值。
         */
        startTrans: function(jndi, isolation) {
          var conn = DbUtil.getConnection(request, (jndi || null));
          if (conn.getAutoCommit()) {
            conn.setAutoCommit(false);
            if (isolation)
              conn.setTransactionIsolation(isolation);
          }
        },
        /**
         * 提交jndi指定的数据库连接的事务,如果指定的连接事务已经提交或回滚则该方法没有任何效果。
         * @param {String} [jndi] 数据库jndi变量名称。为空表示使用默认使用库。
         */
        commit: function(jndi, isolation) {
          var conn = DbUtil.getConnection(request, (jndi || null));
          if (!conn.getAutoCommit()) {
            conn.commit();
          }
        },
        /**
         * 回滚jndi指定的数据库连接的事务,如果指定的连接事务已经提交或回滚则该方法没有任何效果。
         * @param {String} [jndi] 数据库jndi变量名称。为空表示使用默认使用库。
         */
        rollback: function(jndi, isolation) {
          var conn = DbUtil.getConnection(request, (jndi || null));
          if (!conn.getAutoCommit()) {
            conn.rollback();
          }
        },
        /**
         * 判断当前请求对指定模块是否可以访问。
         * @param {String} path 模块路径或其捷径。
         * @return {Boolean} true可以访问,false不可以访问。
         */
        perm: function(path) {
          return WbUtil.canAccess(request, path);
        },
        /**
         * 删除树型结构表中的指定键值的记录。当记录被删除时,其树型结构的所有子节点记录均将被删除。
         * 该方法将使用指定jndi的共享数据库连接,并默认启用事务。
         * @param {String} tableName 数据库表名。
         * @param {String} keyName 主键ID字段名称。
         * @param {String} parentKeyName 上级ID字段名称。
         * @param {Array} delKeys 删除的主键值列表。
         * @param {String} [extraFields] 返回的附加字段名称列表,多个字段以逗号分割。
         * @param {String} [jndi] 数据库jndi名称。
         * @return {JSONArray} 包括子节点在内的所有被删除节点记录值组成的数据列表。
         */
        delTree: function(tableName, keyName, parentKeyName, delKeys, extraFields, jndi) {
          var rs, delRecs, config;
          if (extraFields)
            extraFields = ',' + extraFields;
          else
            extraFields = '';
          if (jndi)
            config = {
              jndi: jndi
            };
          else config = null;
          rs = app.run('select ' + keyName + ',' + parentKeyName + extraFields + ' from ' + tableName, config);
          delRecs = Wb.reverse(Wb.travelTree(rs, keyName, parentKeyName, delKeys));
          app.run('delete from ' + tableName + ' where ' + keyName + '={?' + keyName + '?}', {
            arrayData: delRecs
          });
          return delRecs;
        },
        /**
         * 根据当前语言种类格式化字符串。
         * @param {String} format 模板字符串。
         * @param {String...} values 格式化填充的字符串内容列表。
         * @return {String} 格式化后的字符串。
         */
        format: function() {
          return Str.format(request, arguments[0], [].slice.call(arguments, 1));
        },
        /**
         * 根据客户端当前语言代码,获取langs中对应值组成的对象。
         * @param {Object} langs 按不同语言定义的值对象。
         * @return {Object} 根据客户端语言提取值后组成的对象。
         */
        getLang: function(langs) {
          var list = {},
            lang = Str.getLanguage(request);
          Wb.each(langs, function(k, v) {
            list[k] = v[lang];
          });
          return list;
        },
        /**
         * 运行SQL语句,并获取返回结果对象。
         * @param {String} sql 运行的SQL语句。
         * @param {Object} [config] 配置对象。
         * @param {String} config.jndi 数据库连接jndi。
         * @param {String} config.arrayName 执行批处理时,指定数据源来自request中存储的该变量。
         * @param {JSONArray/Array/String} config.arrayData 执行批处理时,指定数据源来自该值。
         * @param {Boolean} config.batchUpdate 是否允许批处理操作。
         * @param {String} config.errorText 当该值不为空且查询结果集不为空,系统将抛出该信息的异常。
         * @param {String} config.type  执行何种SQL操作,可为"query","update","execute","call",默认为自动。
         * @param {String} config.transaction 执行何种数据库事务操作,可为"start","commit","none"。
         * @param {String} config.isolation 数据库事务隔离级别,可为"readCommitted","readUncommitted","repeatableRead","serializable"。
         * @param {Boolean} config.uniqueUpdate 指定插入、更改或删除记录操作是否有且只有1条。
         * @return {Object} 运行SQL语句获得的结果,可能值为结果集,影响记录数或输出参数结果Map。
         */
        run: function(sql, config) {
          var arrayData,
            query = new com.wb.tool.Query();
          if (config && config.arrayData) {
            arrayData = config.arrayData;
            if (!(arrayData instanceof JSONArray)) {
              if (Wb.isArray(arrayData))
                arrayData = Wb.reverse(arrayData);
              else
                arrayData = new JSONArray(arrayData);
              config.arrayData = arrayData;
            }
          }
          Wb.apply(query, {
            request: request,
            sql: sql
          }, config);
          return query.run();
        },
        /**
         * 在共享的request和response或独立虚拟的response上下文中运行指定文件的xwl模块并返回生成的脚本。
         * 使用该方法运行模块不验证权限,不释放模块运行过程中生成的资源,所有资源待主模块运行完成之后释放。
         * @param {String} url 运行的模块url地址。
         * @param {Object} [params] 请求的参数对象。
         * @param {Boolean} [isInvoke] 是否为invoke模式的调用。true只返回闭包部分的脚本,
         * false返回全部模块脚本。如果指定该参数,将在独立虚拟的response内运行。
         * @return {String} 返回的脚本。只有当指定isInvoke参数时才返回运行的脚本。
         */
        execute: function(url, params, isInvoke) {
          if (isInvoke === undefined) {
            if (params)
              WebUtil.applyAttributes(request, Wb.reverse(params));
            WbUtil.run(url, request, response);
          } else {
            return WbUtil.run(url, Wb.reverse(params), request, !!isInvoke);
          }
        },
        /**
         * 使用com.wb.tool.DataProvider直接输出数据到客户端,详见该控件说明。
         * @param {ResultSet} rs 结果集。
         * @param {Object} [config] 配置参数对象,见com.wb.tool.DataProvider控件的使用。
         * @param {Boolean} [directOutput] 是否立即输出内容到客户端,true立即输出,false从该方法返回内容。默认为false。
         * @return {String/undefined} 输出脚本或undefined。
         */
        dp: function(rs, config, directOutput) {
          var dp = new com.wb.tool.DataProvider();
          Wb.apply(dp, {
            request: request,
            response: response,
            resultSet: rs
          }, config);
          if (directOutput)
            dp.output();
          else
            return dp.getScript();
        },
        /**
         * 从数据库获取数据,并输出指定格式的脚本、图片或流数据至客户端。
         * @param {String} sql sql语句。
         * @param {Object} [config] 配置参数对象,见DataProvider控件的使用。
         * @param {Boolean} [returnScript] 是否返回脚本,true返回生成的脚本,false直接输出,默认为false。
         * @return {String/undefined} 输出脚本或undefined。
         */
        output: function(sql, config, returnScript) {
          var configText, newConfig, dp = new com.wb.controls.DpControl();
          dp.request = request;
          dp.response = response;
          if (config) {
            newConfig = {};
            Wb.each(config, function(key, value) {
              if (Wb.isObject(value))
                value = Wb.encode(value);
              else value = String(value);
              newConfig[key] = value;
            });
            configText = Wb.encode(newConfig);
          } else
            configText = '{}';
          dp.configs = new JSONObject(configText);
          dp.configs.put('sql', sql);
          if (returnScript)
            return dp.getContent(false);
          else
            dp.create();
        },
        /**
         * 获取由SQL生成的数据对象。详见app.output方法的说明。
         * @param {String} sql sql语句。
         * @param {Object} [config] 配置参数对象,见DataProvider控件的使用。
         * @return {Object} 数据对象。
         */
        getData: function(sql, config) {
          return Wb.decode(app.output(sql, config, true));
        },
        /**
         * 获取SQL生成的数据集首行所有字段名称和值组成的对象。如果首行不存在返回null。
         * @param {String} sql sql语句。
         * @param {Object} [config] 配置对象。详见app.run方法的config参数。
         * @return {Object} 记录数据组成的对象或null。
         */
        getRecord: function(sql, config) {
          return app.getRecords(sql, config, true);
        },
        /**
         * 获取SQL生成的数据集所有行所有字段值组成的数组。数组内每一项为记录数据对象。
         * @param {String} sql sql语句。
         * @param {Object} [config] 配置对象。详见app.run方法的config参数。config.count属性指定返回最大记录数。
         * @param {Boolean} [firstRow] 是否仅获取首行记录组成的对象。默认为false。
         * @return {Array} 所有记录数据组成的数组。
         */
        getRecords: function(sql, config, firstRow) {
          var rs, st;
          try {
            rs = app.run(sql, config);
            st = rs.getStatement();
            if (firstRow) {
              if (rs.next())
                return Wb.getRecord(rs);
              else
                return null;
            } else
              return Wb.getRecords(rs, config ? config.count : -1);
          } finally {
            DbUtil.close(rs);
            DbUtil.close(st);
          }
        },
        /**
         * 执行上下文绑定的insert, update, delete数据库更新操作。
         * @param {Object} config 配置参数对象,见Updater控件的使用。
         */
        update: function(configs) {
          var updater = new com.wb.tool.Updater();

          if (Wb.isObject(configs.fieldsMap))
            configs.fieldsMap = Wb.toJSONObject(configs.fieldsMap);
          Wb.apply(updater, {
            request: request
          }, configs);
          updater.run();
        }
      };