
wallbase.tv作者
能力说明:
精通JVM运行机制,包括类生命、内存模型、垃圾回收及JVM常见参数;能够熟练使用Runnable接口创建线程和使用ExecutorService并发执行任务、识别潜在的死锁线程问题;能够使用Synchronized关键字和atomic包控制线程的执行顺序,使用并行Fork/Join框架;能过开发使用原始版本函数式接口的代码。
能力说明:
基本的计算机知识与操作能力,具备Web基础知识,掌握Web的常见标准、常用浏览器的不同特性,掌握HTML与CSS的入门知识,可进行静态网页的制作与发布。
能力说明:
熟练掌握Docker各类高级特性,包括容器数据卷、DockerFile构建等;熟练使用Docker封装MySQL、Redis、Tomcat、Apache等镜像,并可在公有云或私有云部署并保持稳定运行。
能力说明:
掌握Linux文件管理方式和技巧,对用户和组管理有基本认知,掌握Linux网络知识,对TCP/IP协议及OSI七层模型有较为清晰的概念,掌握Linux磁盘与文件系统管理技巧,知道如何安装Linux软件包,逐步掌握Shell脚本的编程技巧。
能力说明:
具备数据库基础知识,了解数据库的分类,具备安装MySQL数据库的能力,掌握MySQL数据类型知识,基本了解常用SQL语句,对阿里云数据库产品有基本认知。
阿里云技能认证
详细说明
java数据脱敏处理,手机号,身份证号和银行卡号打码展示 结果图如下 20:17:51.730 [main] INFO com.lockbur.open.AppTest - 手机号 136****4724 20:17:51.740 [main] INFO com.lockbur.open.AppTest - 手机号 13**** 20:17:51.741 [main] INFO com.lockbur.open.AppTest - 邮箱 845****22@qq.com 20:17:51.741 [main] INFO com.lockbur.open.AppTest - 邮箱不够四位 22****@qq.com 20:17:51.742 [main] INFO com.lockbur.open.AppTest - 邮箱错误 22qq.com 20:17:51.742 [main] INFO com.lockbur.open.AppTest - 假身份证号 132****99308084911 1 先引入commons-lang3,这个基本每个项目都用到 <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.7</version> </dependency> 2 数据处理 这里主要用StringUtils工具的overlay(String str,String overlay,int start,int end)方法可以在指定位置进行字符序列替换,从start索引处开始(包含)到end-1索引处为止进行替换 package com.lockbur.open.utils; import org.apache.commons.lang3.StringUtils; /** * 数据隐私显示 手机号,身份证号和银行卡号等 * @author wangkun23 */ public class PrivacyDimmer { private static final String OVERLAY = "****"; private static final int START = 3; private static final int END = 7; /** * 139****0504 * * @param content * @return */ public static String maskMobile(String content) { if (StringUtils.isEmpty(content)) { return ""; } return StringUtils.overlay(content, OVERLAY, START, END); } /** * 过滤邮箱账号 * 132****99308084911 * * @param email * @return */ public static String maskEmail(String email) { if (StringUtils.isEmpty(email)) { return ""; } String at = "@"; if (!email.contains(at)) { return email; } /** * 这里主要逻辑是需要保留邮箱的注册商 比如@qq.com */ int length = StringUtils.indexOf(email, at); String content = StringUtils.substring(email, 0, length); String mask = StringUtils.overlay(content, OVERLAY, START, END); return mask + StringUtils.substring(email, length); } /** * 身份证打码操作 * 132****99308084911 * @param idCard * @return */ public static String maskIdCard(String idCard) { if (StringUtils.isEmpty(idCard)) { return ""; } return StringUtils.overlay(idCard, OVERLAY, START, END); } } 3测试结果 @Test public void overlay() { logger.info("手机号 {}", PrivacyDimmer.maskMobile("13661014724")); logger.info("手机号 {}", PrivacyDimmer.maskMobile("13")); logger.info("邮箱 {}", PrivacyDimmer.maskEmail("845885222@qq.com")); logger.info("邮箱不够四位 {}", PrivacyDimmer.maskEmail("22@qq.com")); logger.info("邮箱错误 {}", PrivacyDimmer.maskEmail("22qq.com")); logger.info("假身份证号 {}",PrivacyDimmer.maskIdCard("132128199308084911")); }
在阿里云centos7.4上配置nginx免费的https证书,支持泛解析 一 原理说明: 使用acme.sh工具来生成证书,但为了方面采用的使用dns添加TXT记录验证方式,跟传统的webroot有区别。这种方式更简单,快速。 二 效果如下: 根证书域名 https://wallbase.tv 泛解析域名 https://www.wallbase.tv 图片证明 三 准备工作或者必要条件 1 购买阿里云的的ecs服务器,并且在阿里云已经购买域名 2 阿里云开通accessKey,该accessKey需要有操作管理dnsapi的权限,注意:acme使用的letsencript为了验证域名是否是你自己拥有,会通过在域名解析记录中添加一条TXT的解析记录来做为验证。 3 我所有操作都是/root/.acme.sh目录下执行的 四 实施步骤 1 安装nginx ## 安装依赖组件,openssl-devel是必须的,因为nginx要使用ssl指令 # yum install -y gcc gcc-c++ # yum install –y openssl-devel pcre-devel zlib-devel # wget http://nginx.org/download/nginx-1.10.3.tar.gz # tar -zxvf nginx-1.10.3.tar.gz # cd nginx-1.10.3 # ./configure --prefix=/export/nginx-1.10.3 --with-http_stub_status_module --with-http_ssl_module # make && make install 2 安装acme证书签发工具 ## 参考文档https://github.com/Neilpang/acme.sh # pwd /root # curl https://get.acme.sh | sh 3 使用dns_ali生成证书 这里需要把阿里云的accessKey访问设置环境变量中,因为acme在执行过程会去调用阿里云的dnsapi接口,添加TXT的解析记录,在生成证书完毕后,会删除该记录,在解析列表中没有,但是你可以从解析历史记录中查看的历史数据的。 说名:一般不需要写www.wallbase.tv而是使用*.wallbase.tv代替所有的泛解析域名。dns_ali表示使用的阿里云dns,当然acme还支持很多的dns提供商。 # export Ali_Key="LTAItQaq21eiKR66" # export Ali_Secret="***********" # ./acme.sh --issue --dns dns_ali -d wallbase.tv -d *.wallbase.tv ## 安装证书文件/root/.acme.sh/ssl/wallbase.tv # mkdir -p ssl/wallbase.tv # ./acme.sh --installcert -d wallbase.tv -d *.wallbase.tv --key-file ssl/wallbase.tv/privkey.pem --fullchain-file ssl/lockbur.com/fullchain.pem 官方说明: The Ali_Key and Ali_Secret will be saved in ~/.acme.sh/account.conf and will be reused when needed. 意思就是你第一次执行后,acem会保留的的账号信息。所有你执行完毕,就可以使用如下命令删除环境变量: # env # unset Ali_Key # unset Ali_Secret 执行完上面的步骤之后,会在你的/root/.acme.sh/ssl/wallbase.tv目录生产两个文件privkey.pem和fullchain.pem。没错这就是证书文件了。 五 把证书安装到nginx上去 1 第一个 server指令监听的80端口,把所有http的访问重定向到https。 2 nginx配置的关键指令 listen 443 ssl;监听443端口。 3 指定证书文件的位置。这里证书文件可以泛解析,我二级域名都用同一个证书。 ssl_certificate /root/.acme.sh/ssl/wallbase.tv/fullchain.pem; ssl_certificate_key /root/.acme.sh/ssl/wallbase.tv/privkey.pem; 4 nginx的配置文件如下: server { listen 80; server_name localhost; rewrite ^(.*)$ https://$host$1 permanent; } server { server_name wallbase.tv www.wallbase.tv alpha.wallbase.tv; listen 443 ssl; ssl_certificate /root/.acme.sh/ssl/wallbase.tv/fullchain.pem; ssl_certificate_key /root/.acme.sh/ssl/wallbase.tv/privkey.pem; location / { limit_conn perip 10; proxy_pass http://localhost:3000; } location /api { limit_conn perip 10; proxy_pass http://localhost:1062; } location ~* \.(eot|ttf|woff|svg)$ { limit_conn perip 10; add_header Access-Control-Allow-Origin *; proxy_pass http://localhost:3000; } } server { server_name assets.wallbase.tv; listen 443 ssl; ssl_certificate /root/.acme.sh/ssl/wallbase.tv/fullchain.pem; ssl_certificate_key /root/.acme.sh/ssl/wallbase.tv/privkey.pem; location / { root html; } location ~* \.(eot|ttf|woff|svg)$ { limit_conn perip 10; add_header Access-Control-Allow-Origin *; } } 5 最后还有关键一步,重启nginx /export/nginx-1.10.3/sbin/nginx -s reload 然后去访问你的页面,看看是不是有点炫酷了。
我的博客即将入驻“云栖社区”,诚邀技术同仁一同入驻。
gradle项目转换成maven的pom.xml文件 这是我build.gradle的文件: buildscript { repositories { mavenCentral() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:1.3.3.RELEASE") } } apply plugin: 'java' apply plugin: 'jacoco' apply plugin: 'spring-boot' apply plugin: 'maven' sourceCompatibility = 1.8 compileJava.options.encoding = 'UTF-8' group = 'com.101tec' // Options for naming the JAR file archivesBaseName = 'trackr-backend' version = '1.0' if(project.hasProperty('teamcity')) { version += '-build-' + project.teamcity['build.number'] } else { version += '-localbuild' } sourceCompatibility = 1.8 repositories { mavenCentral() } springBoot { executable = true } dependencies { compile "org.springframework.boot:spring-boot-starter-data-rest" compile "org.springframework.boot:spring-boot-starter-data-jpa" compile "org.springframework.boot:spring-boot-starter-mail" compile "org.springframework.boot:spring-boot-starter-integration" compile "org.springframework.boot:spring-boot-starter-security" // not included in boot compile "org.springframework.integration:spring-integration-mail:4.2.5.RELEASE" compile "org.springframework.security.oauth:spring-security-oauth2:2.0.11.RELEASE" compile "com.h2database:h2" compile "postgresql:postgresql:9.1-901.jdbc4" compile "org.flywaydb:flyway-core" compile("org.xhtmlrenderer:flying-saucer-pdf-itext5:9.0.6") compile("org.thymeleaf:thymeleaf:2.1.3.RELEASE") compile "org.projectlombok:lombok:1.12.4" compile "org.glassfish:javax.json:1.0" testCompile "org.springframework.boot:spring-boot-starter-test" testCompile("org.echocat.jomon:testing:1.4.3") { exclude group: "org.mockito" } testCompile "org.mockito:mockito-core:1.9.5" testCompile "com.jayway.jsonpath:json-path" testCompile "org.apache.httpcomponents:httpclient" } task wrapper(type: Wrapper) { gradleVersion = '2.2' } task createPom << { pom { project { groupId 'com.lockbur' artifactId 'lockbur-server' version '1.0.0-SNAPSHOT' inceptionYear '2008' licenses { license { name 'The Apache Software License, Version 2.0' url 'http://www.apache.org/licenses/LICENSE-2.0.txt' distribution 'repo' } } } }.writeTo("$buildDir/pom.xml") } 你可把这个createPom的task改成任何你自己喜欢的名字,如createPom、mavenPom、testPom等,然后只需要执行 gradle clean 或者 grale build 甚至更简单的执行 gradle createPom 执行成功如图 生成后的pom文件如下: <?xml version="1.0" encoding="UTF-8"?> <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <modelVersion>4.0.0</modelVersion> <groupId>com.lockbur</groupId> <artifactId>lockbur-server</artifactId> <version>1.0.0</version> <inceptionYear>2008</inceptionYear> <licenses> <license> <name>The Apache Software License, Version 2.0</name> <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url> <distribution>repo</distribution> </license> </licenses> <dependencies> <dependency> <groupId>org.springframework.integration</groupId> <artifactId>spring-integration-mail</artifactId> <version>4.2.5.RELEASE</version> <scope>compile</scope> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>compile</scope> </dependency> <dependency> <groupId>org.xhtmlrenderer</groupId> <artifactId>flying-saucer-pdf-itext5</artifactId> <version>9.0.6</version> <scope>compile</scope> </dependency> <dependency> <groupId>postgresql</groupId> <artifactId>postgresql</artifactId> <version>9.1-901.jdbc4</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.glassfish</groupId> <artifactId>javax.json</artifactId> <version>1.0</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.echocat.jomon</groupId> <artifactId>testing</artifactId> <version>1.4.3</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-rest</artifactId> <scope>compile</scope> </dependency> <dependency> <groupId>org.flywaydb</groupId> <artifactId>flyway-core</artifactId> <scope>compile</scope> </dependency> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf</artifactId> <version>2.1.3.RELEASE</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-integration</artifactId> <scope>compile</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.12.4</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId> <version>2.0.11.RELEASE</version> <scope>compile</scope> </dependency> <dependency> <groupId>com.jayway.jsonpath</groupId> <artifactId>json-path</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> <scope>compile</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>1.9.5</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> <scope>compile</scope> </dependency> </dependencies> </project>
原文链接 https://www.web-tinker.com/article/20310.html WebSocket是很民主的,啥都要协商!建立连接时需要握手协议,连断开连接都需要双方共同完成!其实断开连接直接断开TCP连接就可以了,但是这有点暴力。文明点的方法是发个请求,让对方自己断开。客户端要主动断开就必须向服务器发送8这个操作码。 首先是服务器主导断开的情况,最简单的方法是直接把TCP连接断开,这里就不演示了。由于这对客户端来说是个意外断开,WebSocket对象采取应急措施也触发close事件。咱是文明人,当然要做点有绅士风度的事情。于是咱不从服务器断开连接,而是向客户端发送个请求断开的操作码来请求客户端自己断开。 其实就是个操作码为8的帧。但要注意的是数据部分比较特殊。当然如果嫌麻烦可以不传,不过要是不传就和前面的霸王硬上弓一样无节操了。数据部分的前两个字节是状态码,之后的部分是关闭连接原因的文本描述,这些东西可以传到客户端。 (encodeDataFrame与decodeDataFrame函数见生成数据帧和解析数据帧) //客户端程序 var ws=new WebSocket("ws://127.0.0.1:8000"); ws.onclose=function(e){ console.log(e); ws.close(); //关闭TCP连接 }; ======================================================== //服务器程序 var crypto=require('crypto'); var WS='258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; require('net').createServer(function(o){ var key; o.on('data',function(e){ if(!key){ //握手 key=e.toString().match(/Sec-WebSocket-Key: (.+)/)[1]; key=crypto.createHash('sha1').update(key+WS).digest('base64'); o.write('HTTP/1.1 101 Switching Protocols\r\n'); o.write('Upgrade: websocket\r\n'); o.write('Connection: Upgrade\r\n'); o.write('Sec-WebSocket-Accept: '+key+'\r\n'); o.write('\r\n'); //构造断连请求的数据部分,前面留两字节存放状态码 var buf=new Buffer('\0\0孩子,地球太危险了,快回火星去吧!'); buf.writeUInt16BE(1000,0); //在头两个字节写入一个状态码 //发送断连请求 o.write(encodeDataFrame({FIN:1,Opcode:8,PayloadData:buf})); }; }); }).listen(8000); 客户端会在onclose的参数中接收到一个这样的东西,状态码和结束原因描述分别在code和reason两个参数中。规范文档中规定了很多状态码的含义,不过这个目前不是强制性的,我就不列举了。见RFC6455#section-7.4。客户端在收到服务器的这个断连请求后应该调用close方法来关闭,否则连接会先入停滞状态等待客户端响应。 服务器主导断开的情况就是这样。下面是客户端主导断开的情况。客户端先要调用close方法,这个操作会发送一个断连请求到服务器上,服务器收到这个请求后把TCP连接断开即可。但是服务器程序是自己写的,这个请求也需要自己解析。 //客户端程序 var ws=new WebSocket("ws://127.0.0.1:8000"); ws.onopen=function(){ ws.close(); //发起断连请求 }; ws.onclose=function(e){ console.log(e); }; //服务端程序序 var crypto=require('crypto'); var WS='258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; require('net').createServer(function(o){ var key; o.on('data',function(e){ if(!key){ //握手 key=e.toString().match(/Sec-WebSocket-Key: (.+)/)[1]; key=crypto.createHash('sha1').update(key+WS).digest('base64'); o.write('HTTP/1.1 101 Switching Protocols\r\n'); o.write('Upgrade: websocket\r\n'); o.write('Connection: Upgrade\r\n'); o.write('Sec-WebSocket-Accept: '+key+'\r\n'); o.write('\r\n'); }else{ var frame=decodeDataFrame(e); console.log(frame); if(frame.Opcode==8){ //这里也可以发送个结束包来给客户端的onclose中带参数 //var buf=new Buffer('\0\0孩子,地球太危险了,快回火星去吧!'); //buf.writeUInt16BE(1000,0); //o.write(encodeDataFrame({FIN:1,Opcode:8,PayloadData:buf})); o.end(); //断开连接 }; }; }); }).listen(8000); 总之,客户端直接调用close方法并不会关闭连接,而是发送请求到服务器请求对方。服务器接收请求后可以断开连接。这会触发客户端的close事件。当然,在断开之前也可以发送个同样的断连请求,并包含状态码和原因描述。
原文地址:https://www.web-tinker.com/article/20309.html 前面的例子都是用了1这个操作码(Opcode)来传输文本的。没错,1这个操作码就是传输文本(UTF-8)的。还用到了在分片过程中把操作码设置为0,0也只是分片时用的。操作码是4位的,取值当然不止这两个,除了指定传输数据类型外还有其它用途的操作码。 规范文档中也提供了一个表格 |Opcode | Meaning | Reference | -+--------+-------------------------------------+-----------| | 0 | Continuation Frame | RFC 6455 | -+--------+-------------------------------------+-----------| | 1 | Text Frame | RFC 6455 | -+--------+-------------------------------------+-----------| | 2 | Binary Frame | RFC 6455 | -+--------+-------------------------------------+-----------| | 8 | Connection Close Frame | RFC 6455 | -+--------+-------------------------------------+-----------| | 9 | Ping Frame | RFC 6455 | -+--------+-------------------------------------+-----------| | 10 | Pong Frame | RFC 6455 | -+--------+-------------------------------------+-----------| 目前就这么多个操作码被定义,其它都是预留的。0和1这两个之前的例子用过的就不说了。2这个操作码是告诉服务器不要把数据转换成字符串,直接当做二进制数据来用。这会在传输图片之类的操作中用到,至于传输图片的例子以后再给吧。这篇主要是介绍后三个操作码。 8这个操作码是服务器向客户端请求结束当前连接用的,不要以为这很简单,WebSocket的建立连接需要握手协议,那么关闭也需要双方的协商,还有关闭的代码什么的,总之很麻烦,下回再介绍。 9和10这两个状态码就简单些了,它们是成对的。服务器向客户端发送一个Ping帧时客户端会自动返回一个Pong帧,并且数据部分与发送的完全相同。如果不同或收不到,则说明网络有问题。下面是例子 (encodeDataFrame与decodeDataFrame函数见生成数据帧和解析数据帧) //客户端程序 var ws=new WebSocket("ws://127.0.0.1:8000"); //服务器程序 var crypto=require('crypto'); var WS='258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; require('net').createServer(function(o){ var key; o.on('data',function(e){ if(!key){ //握手 key=e.toString().match(/Sec-WebSocket-Key: (.+)/)[1]; key=crypto.createHash('sha1').update(key+WS).digest('base64'); o.write('HTTP/1.1 101 Switching Protocols\r\n'); o.write('Upgrade: websocket\r\n'); o.write('Connection: Upgrade\r\n'); o.write('Sec-WebSocket-Accept: '+key+'\r\n'); o.write('\r\n'); //握手成功后给客户端发送个Ping包 o.write(encodeDataFrame({FIN:1,Opcode:9,PayloadData:"数据"})); }else{ //解析客户端传过来的数据帧并输出 var data=decodeDataFrame(e); data.PayloadData=data.PayloadData.toString(); console.log(e); }; }); }).listen(8000); Chrome调试工具截图: NodeJS控制台截图:
原文地址:https://www.web-tinker.com/article/20307.html 昨天的文章中介绍了WebSocket数据帧的结构和解析。其实对从服务器发送往客户端的数据也是同样的数据帧。但因此觉得这看似和解析数据帧一样简单那就错了。我们需要自己去生成数据帧。而且会遇上和解析时候不同的问题,比如数据帧分片传输的情况。 从服务器发送到客户端的数组帧不需要掩码,这是非常值得庆幸的地方。于是要写出一个生成数据帧的函数并不难 //NodeJS function encodeDataFrame(e){ var s=[],o=new Buffer(e.PayloadData),l=o.length; //输入第一个字节 s.push((e.FIN<<7)+e.Opcode); //输入第二个字节,判断它的长度并放入相应的后续长度消息 //永远不使用掩码 if(l<126)s.push(l); else if(l<0x10000)s.push(126,(l&0xFF00)>>8,l&0xFF); else s.push( 127, 0,0,0,0, //8字节数据,前4字节一般没用留空 (l&0xFF000000)>>24,(l&0xFF0000)>>16,(l&0xFF00)>>8,l&0xFF ); //返回头部分和数据部分的合并缓冲区 return Buffer.concat([new Buffer(s),o]); }; 可以把它用于一个实例中 //客户端程序 var ws=new WebSocket("ws://127.0.0.1:8000/"); ws.onmessage=function(e){ console.log(e); }; //服务器程序 var crypto=require('crypto'); var WS='258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; require('net').createServer(function(o){ var key; o.on('data',function(e){ if(!key){ //握手 key=e.toString().match(/Sec-WebSocket-Key: (.+)/)[1]; key=crypto.createHash('sha1').update(key+WS).digest('base64'); o.write('HTTP/1.1 101 Switching Protocols\r\n'); o.write('Upgrade: websocket\r\n'); o.write('Connection: Upgrade\r\n'); o.write('Sec-WebSocket-Accept: '+key+'\r\n'); o.write('\r\n'); //握手成功后给客户端发送个数据 o.write(encodeDataFrame({ FIN:1,Opcode:1,PayloadData:"次碳酸钴" })); }; }); }).listen(8000); 上面是最基本的用法。但是有时候数据需要分成多个数据包来发送,这就需要用到分片,也就是使用多个数据帧来传输一个数据。分片传输分为三个部分: 开始帧:FIN=0,Opcode>0;一个 传输帧:FIN=0,Opcode=0;零个或多个 终止帧:FIN=1,Opcode=0;一个 FIN是FINAL的缩写,它为1时表示一个数据传输结束,而开始和传输帧的时候数据都没结束,所以是0,之后最后的结束帧FIN是1。同一个数据即使分片传输,它的每个数据帧的Opcode也应该相同,为了避免冲突,只对分片传输的开始帧设置Opcode,传输帧和结束帧的Opcode留0。因此把上面实例的部分代码改成 //握手成功后给客户端发送个数据 o.write(encodeDataFrame({ FIN:0,Opcode:1,PayloadData:"ABC" })); o.write(encodeDataFrame({ FIN:0,Opcode:0,PayloadData:"-DEF-" })); o.write(encodeDataFrame({ FIN:1,Opcode:0,PayloadData:"GHI" })); 就可以在客户端得到 这就是分片传输的关键所在。
原文地址:https://www.web-tinker.com/article/20306.html 知道了怎么握手只是让客户端和服务器建立连接而已,WebSocket真正麻烦的地方是在数据的传输上!为了环保,它使用了特定格式的数据帧,这个数据帧需要自己去解析(当然也有别人编写好的库可以用)。虽然官方文档描述的很详细,但是看起来还是蛋疼。 当客户端向服务器发送一个数据时服务器收到一个数据帧,比如下面的程序: //客户端程序 var ws=new WebSocket("ws://127.0.0.1:8000"); ws.onopen=function(e){ ws.send("次碳酸钴"); //发送数据 };=============================================================== //服务器程序 var crypto=require('crypto'); var WS='258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; require('net').createServer(function(o){ var key; o.on('data',function(e){ if(!key){ //握手 key=e.toString().match(/Sec-WebSocket-Key: (.+)/)[1]; key=crypto.createHash('sha1').update(key+WS).digest('base64'); o.write('HTTP/1.1 101 Switching Protocols\r\n'); o.write('Upgrade: websocket\r\n'); o.write('Connection: Upgrade\r\n'); o.write('Sec-WebSocket-Accept: '+key+'\r\n'); o.write('\r\n'); }else onmessage(e); //接收并交给处理函数 }); }).listen(8000); 这里是直接把接收到的数据输出了,得到这样一个东西 这就是一个完整的数据帧,直接的16进制数据我们当然无法直接阅读,需要按照数据帧的格式把它里面的数据取出来才行。对于这个数据帧,官方文档提供了一个结构图 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-------+-+-------------+-------------------------------+ |F|R|R|R| opcode|M| Payload len | Extended payload length | |I|S|S|S| (4) |A| (7) | (16/64) | |N|V|V|V| |S| | (if payload len==126/127) | | |1|2|3| |K| | | +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + | Extended payload length continued, if payload len == 127 | + - - - - - - - - - - - - - - - +-------------------------------+ | |Masking-key, if MASK set to 1 | +-------------------------------+-------------------------------+ | Masking-key (continued) | Payload Data | +-------------------------------- - - - - - - - - - - - - - - - + : Payload Data continued ... : + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Payload Data continued ... | +---------------------------------------------------------------+ 光拿出这个实在很难看懂,顶部数字用十进制而不是八进制太让人蛋疼了。当然官方文档在后面的描述中也有详细介绍,看完后再回头来看图表才能看明白。其实WebSocket目前还不太完善,很多实验性的东西,所以完全按照官方文档来理解是蛋疼的。这里就说我自己的理解。 现在再看左上角上面的图标,左上角的四个小列,也就是4位,第一位是FIN,后面三位是RSV1到3。官方文档上说RSV是预留的空间,正常为0,这就意味着,正常情况下他们可以当做0填充,那么前4位只有第一位的FIN需要设置,FIN表示帧结束,由于这篇中它不重要就不特别介绍了。接着后面的四位是储存opcode的值,这个opcode是标识数据类型的。这样数据的第一个字节我们就能理解它的含义了,看上面16进制的数据的第一个字节81换成二进制是1000001,第一个1是FIN的值,最后一个1是opcode的值。 接着是第二个字节的数据,它由1位的MASK和7位的PayloadLen组成,MASK标识这个数据帧的数据是否使用掩码,PayloadLen表示数据部分的长度。但是PayloadLen只有7位,换成无符号整型的话只有0到127的取值,这么小的数值当然无法描述较大的数据,因此规定当数据长度小于或等于125时候它才作为数据长度的描述,如果这个值为126,则时候后面的两个字节来储存储存数据长度,如果为127则用后面八个字节来储存数据长度。所以上面的图片第一行的最右侧那块和第二行看起来有些颓然。从我们的示例数据来看,第二个字节的8C中80是最高位为1,这意味着MASK为1,后面的C表示这个数据部分有12个字节。 再接着是上面图表中的MaskingKey,它占四个字节,储存掩码的实体部分。但是只有在前面的MASK被设置为1时候才存在这个数据,否则不使用掩码也就没有这个数据了。看我们的示例数据,由于前面的MASK为1,所以3到6字节的“79 77 3d 41”是数据的掩码实体。 最后是数据部分,如果掩码存在,那么所有数据都需要与掩码做一次异或运算,四个字节的掩码与所有数据字节轮流发生性关系。如果不存在掩码,那么后面的数据就可以直接使用。 这样数据帧就解析完了。下面是我写的数据帧解析的程序,请不要吐槽代码没优化 function decodeDataFrame(e){ var i=0,j,s,frame={ //解析前两个字节的基本数据 FIN:e[i]>>7,Opcode:e[i++]&15,Mask:e[i]>>7, PayloadLength:e[i++]&0x7F }; //处理特殊长度126和127 if(frame.PayloadLength==126) frame.PayloadLength=(e[i++]<<8)+e[i++]; if(frame.PayloadLength==127) i+=4, //长度一般用四字节的整型,前四个字节通常为长整形留空的 frame.PayloadLength=(e[i++]<<24)+(e[i++]<<16)+(e[i++]<<8)+e[i++]; //判断是否使用掩码 if(frame.Mask){ //获取掩码实体 frame.MaskingKey=[e[i++],e[i++],e[i++],e[i++]]; //对数据和掩码做异或运算 for(j=0,s=[];j<frame.PayloadLength;j++) s.push(e[i+j]^frame.MaskingKey[j%4]); }else s=e.slice(i,i+frame.PayloadLength); //否则直接使用数据 //数组转换成缓冲区来使用 s=new Buffer(s); //如果有必要则把缓冲区转换成字符串来使用 if(frame.Opcode==1)s=s.toString(); //设置上数据部分 frame.PayloadData=s; //返回数据帧 return frame; }; 既然有了解析程序,那么我们就可以把上面实例服务器端的onmessage方法修改一下 function onmessage(e){ e=decodeDataFrame(e); //解析数据帧 console.log(e); //把数据帧输出到控制台 }; 这样服务器接收客户端穿过了的数据就没问题了。嘛,这篇文章就只说接收,至于从服务器发送到客户的情况会有更复杂的情况出现,咱下一篇再说。
原文链接 https://www.web-tinker.com/article/20305.html WebSocket(壹) 握手连接 WebSocket虽然很先进,很好用,但却是个很麻烦的东西。与普通的Web通信机制不同,它本身可以算是一个协议。要使用WebSocket首先得让客户端和服务器建立连接,而且这个连接蛋疼的比TCP那样的传输层协议还复杂,需要通过验证KEY来做握手工作。 这个握手协议使用的是HTTP格式的请求,并再头部分带上一个Sec-WebSocket-Key字段,服务器对这个字段加上一个特定的字符串后做一次sha1运算,然后把结果用Base64的形式以同样的方式发送回去就可以完成握手的工作了。当然,WebSocket这个协议还在不断完善更新,目前这么用是没问题的,历史版本中还出现过其它方式,这里就无视他们了。 下面是足以完成握手的服务器端程序,使用NodeJS。var crypto=require('crypto'); var WS='258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; require('net').createServer(function(o){ var key; o.on('data',function(e){ if(!key){ //获取发送过来的KEY key=e.toString().match(/Sec-WebSocket-Key: (.+)/)[1]; //连接上WS这个字符串,并做一次sha1运算,最后转换成Base64 key=crypto.createHash('sha1').update(key+WS).digest('base64'); //输出返回给客户端的数据,这些字段都是必须的 o.write('HTTP/1.1 101 Switching Protocols\r\n'); o.write('Upgrade: websocket\r\n'); o.write('Connection: Upgrade\r\n'); //这个字段带上服务器处理后的KEY o.write('Sec-WebSocket-Accept: '+key+'\r\n'); //输出空行,使HTTP头结束 o.write('\r\n'); }else{ //数据处理 }; }); }).listen(8000); 客户端一段简单的代码即可 var ws=new WebSocket("ws://127.0.0.1:8000"); ws.onerror=function(e){ console.log(e); }; ws.onopen=function(){ console.log("握手成功"); }; 一旦握手成功就会触发open事件,否则触发error事件。在open之后就可以进行数据传输了,至于数据要如何传输这比这个握手连接要复杂的多,咱下一篇再说。
1 我一直使用flex4和flash开发视频聊天的程序,以前再flex中和flash直接写上服务器路径然后指定play的流的名称就可以了。代码如下: // Check for reconnect. if ( nsPlay != null ) { // Stop and close previous NetStream. var stopStreamEvent : StopStreamEvent = new StopStreamEvent(); stopStreamEvent.dispatch(); } // Setup NetStream for playback. nsPlay = new NetStream( main.media.nc ); nsPlay.bufferTime = bufferTime; nsPlay.receiveAudio( audio ); nsPlay.receiveVideo( video ); nsPlay.client = responder; main.media.videoRemote = new Video( main.cameraSettings.width, main.cameraSettings.height ); main.media.videoRemote.attachNetStream( nsPlay ); main.playbackState = true; nsPlay.play( streamName ); 上面的stream就是stream1459921140010 你会看到我再接下来的c++使用ffplay去播放也是这样写的。 2 使用ffplay播放直播流:注意再这个后面多加了一个参数live=1,而且必须要加该参数,否则再red5上你能点播但是不能直播 D:\Tools\ffmpeg>ffplay -i "rtmp://localhost/oflaDemo/stream1459921140010 live=1" 3 说明之前看到一个博客中提到http://blog.csdn.net/qiuchangyong/article/details/18862247需要修改red5源码,我修改之后编译还是不可以,而且他代码就有问题start_time是long数据,怎么和-2比较的,这样语法就会报错。不多说这个了。 4 实例演示 播放情况 发布情况
flex4 中获取Microphone,抑制回声和使用Speex编码音频的方法,该方法经过本人测试可用。 一、如果你不关心请直接把代码复制,如果想要了解细节的请继续往下看 private function getMicrophone(codec:String):Microphone { var mic:Microphone = null; mic = Microphone.getEnhancedMicrophone(); if (mic) { var options:MicrophoneEnhancedOptions = new MicrophoneEnhancedOptions(); options.mode = MicrophoneEnhancedMode.FULL_DUPLEX; options.autoGain = false; options.echoPath = 128; options.nonLinearProcessing = true; microphone.enhancedOptions=options; mic.setUseEchoSuppression(true); } else { mic = Microphone.getMicrophone(); } if (mic == null) { trace("No microphone! <o>"); } else { mic.addEventListener(StatusEvent.STATUS, onMicStatusEvent); mic.setLoopBack(false); mic.setSilenceLevel(0, 20000); mic.gain = 60; if (codec == "SPEEX") { mic.encodeQuality = 8; mic.codec = SoundCodec.SPEEX; mic.framesPerPacket = 2; mic.rate = 16; trace("Using SPEEX wideband codec"); } else { mic.codec = SoundCodec.NELLYMOSER; mic.rate = 8; trace("Using Nellymoser codec"); } } return mic; } protected function onMicStatusEvent(event:StatusEvent):void { trace("New microphone status event"); //trace(ObjectUtil.toString(event)); switch (event.code) { case "Microphone.Muted": break; case "Microphone.Unmuted": break; default: break; } } 二、针对上面的参数进行说明. 1、Microphone提供了 getEnhancedMicrophone 方法(最好是升级到flex4.6以上,flex3肯定是没有这个api的),能够返回一个“增强麦克风",ADOBE声称可以用来创建不用头戴式耳机麦克风的音视频应用程序,用这个效果比之前的Microphone.getMicrophone()都要很多改善,包括获取音频和音频编码,还有抑制回声等. 2、mic.setLoopBack(false); 设置为false 就是禁止将麦克风捕获的音频传送到本地扬声器.如果不设置为false,你会听到自己说话的声音的。默认值是true。 3、mic.setUseEchoSuppression(true);指定是否使用音频编解码器的回音抑制功能。除非用户已经在 Flash Player 的“麦克风设置”面板中选择了“降低回音”,否则默认值为false。 三、参考资料文献, 1、Adobe® Flex® 4 Beta 语言参考 http://www.fising.cn/docs/AS3_Reference/flash/media/Microphone.html 2、参照bigbluebutton远程教育系统的源码 https://github.com/bigbluebutton/bigbluebutton/blob/master/clients/flash/common-library/src/org/bigbluebutton/lib/voice/services/VoiceStreamManager.as 3、 Flex客户端设置speex编码时Red5对音频数据的处理以及将speex解码
一 概要说明 使用nginx搭建流媒体直播平台,目的就是要支持rtmp协议,实现用户使用rtmp(rtmp://192.168.201.128/myapp)协议推送流到服务器。然后其他用户点播该用户推送的视频流信息。既然是rtmp协议,所以客户端可以是flash程序,也可以OBS(Open Broadcaster Software)这种比较大众化的直播客户端。个人是比较喜欢使用OBS的,老实说我其实也是flash和flex开发者,开发个多款WEB视频程序和视频会议系统。java水平也是很高的。欢迎打脸,我这种人就不怎么谦虚,因为我觉太谦虚就虚伪了. 再世面上有很多流媒体服务器。有商业的也有开源,比如常用FMS,Red5,wowza.crtmpserver,等,如果是做小型视频会议,我个人强烈推荐Red5。Red5有很开放的api,对于开发实时性要求比较高的很方便。开发工具和开发java的人上手也很快。 二 环境准备 1 准备一台linux的操作系统,我的Centos5。windows 再nignx上自己编译模块很麻烦的,所以我就在Centos上测试。我的系统信息如下: Linux localhost 2.6.18-128.el5 #1 SMP Wed Jan 21 10:44:23 EST 2009 i686 athlon i386 GNU/Linux 2 准备软件包 nginx-1.4.7.tar.gz nginx-rtmp-module-1.1.7.tar.gz 三 开始安装 1 安装nginx所需要的依赖包。注意不同系统或者模块需要的依赖包是不一样的。我这里值安装最基本的就行了。 [root@localhost html]# yum install -y gcc gcc-c++ [root@localhost html]# yum install –y openssl-devel pcre-devel zlib-devel注意:最好不要用默认的yum源。都统一换成阿里云的yum源。2 先解压包,这不没什么难度吧。然后执行ningx配置文件。执行没问题后,执行编译,安装 [root@localhost local]# tar -zvxf nginx-1.4.7.tar.gz [root@localhost local]# wget https://github.com/arut/nginx-rtmp-module/archive/v1.1.7.tar.gz [root@localhost local]# tar nginx-rtmp-module-1.1.7.tar.gz [root@localhost local]# tar -zxvf nginx-rtmp-module-1.1.7.tar.gz [root@localhost local]# cd /usr/local/nginx-1.4.7 ./configure --prefix=/usr/local/nginx --add-module=/usr/local/nginx-rtmp-module-1.1.7 [root@localhost nginx-1.4.7]# make && make install 输出日志我就不贴出来了。编译完成后。就该修改nginx的配置文件,让nginx支持rtmp协议。 3 修改配置文件后内容如下(改配置文件可以参考): [root@localhost test]# pwd /usr/local/nginx-rtmp-module-1.1.7/test [root@localhost test]# ll total 56 -rwxrwxr-x 1 root root 49 Mar 24 2015 dump.sh -rwxrwxr-x 1 root root 84 Mar 24 2015 ffstream.sh -rw-rw-r-- 1 root root 1245 Mar 24 2015 nginx.conf -rwxrwxr-x 1 root root 59 Mar 24 2015 play.sh -rw-rw-r-- 1 root root 499 Mar 24 2015 README.md drwxrwxr-x 2 root root 4096 Mar 24 2015 rtmp-pu 为了方便我把我的ngin的配置文件完整的贴出来: #user nobody; worker_processes 1; #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; #pid logs/nginx.pid; events { worker_connections 1024; } rtmp { server { listen 1935; application myapp { live on; #record keyframes; #record_path /tmp; #record_max_size 128K; #record_interval 30s; #record_suffix .this.is.flv; #on_publish http://localhost:8080/publish; #on_play http://localhost:8080/play; #on_record_done http://localhost:8080/record_done; } } } http { include mime.types; default_type application/octet-stream; #access_log logs/access.log main; sendfile on; #tcp_nopush on; #keepalive_timeout 0; keepalive_timeout 65; #gzip on; server { listen 80; server_name localhost; location / { root html; index index.html index.htm; } #error_page 404 /404.html; error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } } 四 启动服务器,测试 1 执行nginx/sbin/nginx 启动服务。你应该能够看到服务器也启用1935端口,就表示nginx已经支持rtmp推送流了。 [root@localhost conf]# netstat -antpl Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:1935 0.0.0.0:* LISTEN 11908/nginx tcp 0 0 0.0.0.0:111 0.0.0.0:* LISTEN 2674/portmap tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 11908/nginx tcp 0 0 127.0.0.1:631 0.0.0.0:* LISTEN 2965/cupsd tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 2989/sendmail: acce tcp 0 0 0.0.0.0:765 0.0.0.0:* LISTEN 2703/rpc.statd tcp 0 0 192.168.201.128:1935 192.168.201.1:55561 ESTABLISHED 11950/nginx: worker tcp 0 1934 192.168.201.128:1935 192.168.201.1:55575 ESTABLISHED 11950/nginx: worker tcp 0 0 :::22 :::* LISTEN 2950/sshd tcp 0 0 ::ffff:192.168.201.128:22 ::ffff:192.168.201.1:56019 ESTABLISHED 12197/sshd: root@no tcp 0 592 ::ffff:192.168.201.128:22 ::ffff:192.168.201.1:53081 ESTABLISHED 3268/1 2 拷贝你/usr/local/nginx-rtmp-module-1.1.7/test/www 目录下的所有文件放到nginx的html下。这个是rtmp模块提供的测试案例。就是一个flash客户端推送流和一个播放流的案例;看起来如下: [root@localhost www]# cd /usr/local/nginx/html/ [root@localhost html]# ll total 60 -rw-r--r-- 1 root root 537 Feb 5 18:54 50x.html -rw-r--r-- 1 root root 15145 Feb 5 19:05 bg.jpg -rw-r--r-- 1 root root 511 Feb 5 19:06 index.html drwxr-xr-x 2 root root 4096 Feb 5 19:05 jwplayer drwxr-xr-x 2 root root 4096 Feb 5 19:05 jwplayer_old -rw-r--r-- 1 root root 1460 Feb 5 19:07 record.html 五 测试, 1 打开浏览器输入地址:http://192.168.201.128/record.html 这是个推送流的测试地址。打开会提示你允许使用你摄像头。允许就行了。如下 2 打开另外一个浏览器,输入http://192.168.201.128/index.html 这个测试播放地址效果如下: 六 总结。 你可以用OBS来发布到nginx.也是可以的。总的来说这种方式还不错的,但是如果要实时性高,多人视频就不太适用。 参考文献:nginx+nginx-rtmp-module+ffmpeg搭建流媒体服务器 需要安装包和的朋友,可以给mail 845885222#qq.com
使用red5和OBS搞出自己的直播平台来挑战bilibili(B站),为什么文章标题叫这个,其实我学习red5和fms方面的东西是很久了的,加上我最近借助flex,要开发一套医疗远程会诊的系统(web端的视频聊天)。所以我对流媒体使用和环境搭建,还有客户端开发都已经很熟悉了。笔者混迹于B站多年(我大概是是2012年使用哔哩哔哩的,那时候只有弹幕,没有游戏和直播之类的东西),我之前是打算去面试B站做视频直播这块带头人的。可惜我那时候打听的时候人家不做直播方面的考虑。后来我就自己去开发一套图片网站,图片网站毕竟带宽不是很大 不挣钱的情况我还可以承受。所以今天来写个博客就是教大家自己搭建一个自己的专属直播平台,这样子就不怕哔哩哔哩封你房间什么,前提你是会自己技术,服务器管理。会java flash等啊。不会的也可以问我。 一、准备工作 1 在你电脑上安装jdk1.8 。我提供的这个red5必须使用jdk1.8,要不然不能运行 2 下载red5-1.0.6,下载地址: http://download.csdn.net/detail/wk313753744/9374983 3 就是去下载OBS(open broadcast software), 4 你要能看见效果,你还需要准备一个flash播放器。 .二、安装说明 1 安装jdk 2 安装red5-server-1.0.6,首先确保你已经下载好了red5-server-1.0.6.zip,解压 (1 ) 进入到red5-server-1.0.6目录,看起是这样子的。因为新版的red5不在使用jetty作为内置服务器,而是使用tomcat(1.0.6使用的tomcat8容器的),所以看起来tomcat的目录结构很像,conf存放就是一些配置文件,我们主要修改是conf/red5.properties,根据自己需要修改端口号,log是日志目录,webapps下,就是你自己开发的red5的程序了,比如直播,点播,聊天等,共享对象的案例等。 ( 2 ) 在启动red5,我们需要注意一点。如果你系统中的环境默认安装时jdk8,那么你不需要调整,直接进入下一步骤,但是如果你安装了其他版本的jdk,比如常见的jdk1.7.*。那么你就必须修改你red5使用的jdk是jdk1.8以上,找到red5.bat,用文本编辑工具打开,找到32行。 :launchRed5 echo Starting Red5 "%JAVA_HOME%\bin\java" %JAVA_OPTS% -cp "%RED5_CLASSPATH%" %RED5_MAINCLASS% %RED5_OPTS% goto finally把上边%JAVA_HOME% 变量修改成C:\Program Files\Java\jdk1.8.0_05\bin\java 使用你jdk8的绝对路径。下面修改之后的配置文件。 :launchRed5 echo Starting Red5 "C:\Program Files\Java\jdk1.8.0_05\bin\java" %JAVA_OPTS% -cp "%RED5_CLASSPATH%" %RED5_MAINCLASS% %RED5_OPTS% goto finally (3) 启动red5。在window中双击red5.bat 。即可启动。在linux上双击red5.sh,但是在linux中为了不是交互式模式运行,你启动的red5-highperf.sh。这个经过参数优化过的。也可以后台运行。 以上服务器环境就准备好了,下面开始介绍obs怎么发送视频流到red5服务器上, 二、OBS发布流到Red5服务器, (1)打开OBS客户端,安装什么的我就不讲了。主要有两个地方FMS URL和播放路径/串流码(如果存在)。 FMS URL: rtmp://127.0.0.1/live 播放路径/串流码(如果存在): test 注:播放路径/串流码(如果存在)在OBS意思就是playPath。 在red5你理解为一个流名字(streamname)。随便取一个就是了,然后在你播放该流地方制定你现在取得这个名字,我这里就是test了。 (2)OBS设定点击确定之后,回到OBS主界面,在来源区域右键,添加-》显示获取。这样就创建了一个捕捉源,当然也可以使用摄像头作为捕捉源。 (3) 为了保险起见,你如果是第一次使用OBS,那么你应该点击预览串流。看看效果 (4)点击开始串流,就会把你视频流发布到Red5服务器。这里顺便说一句,这里开始串流就跟flash或者flex中的NetStream中publish()方式是一样。 三、预览效果 就是使用rtmp协议来播放你发布的视频流. 我用red5自带的这个案例来播放流,之前那个用自己开发一个flex程序播放是一样的。 主要有两个地方需要需要修改,就是Location中路径要跟你OBS的FMS URL一直,而且你RED5服务器上要有live这个程序,我给你下载red5压缩包中是有的。 第二个就是修改Name 为test,然后点击Play就可以播放了。 最后大功告成欢迎访问我图片网站 http://lockbur.com/
(1)、 org.red5.server.stream.StreamService - Bad nam (xxxx流或者flv文名) [INFO] [RTMPConnectionExecutor-3] org.red5.demos.oflaDemo.Application - oflaDemo appConnect [ERROR] [RTMPConnectionExecutor-2] org.red5.server.stream.StreamService - Bad name stream_consult_74 上面错误时提示我stream_consult_74 我发布的流名字不正确,经过我多次尝试,这个错误的原因你之前已经发布了一个stream_consult_74的流名称,而且你还指定了flash或者flex发布模式时live。换句话说就是你当前的流已经存在了,不能覆盖,就提示Bad name stream_consult_74。 (2)、[WARN] [RTMPConnectionExecutor-2] org.red5.server.scope.Scope - Requested scope: stream_consult_74 is not of IScope type: org.red5.server.scope.BroadcastScope 这个也是非常常见的 原因就是你去获取一个没有发布的流。就好像我需要读取red5.flv。但实际上我的服务器的oflademo下的streams下 并没有一个叫red5.flv的文件。或者没有发布一个red5的流到服务器 也会这个警告。
FastDFS文件系统(二) fastdfs和其他文件系统区别 一、概述 普通存储方案:Rsync、DAS(IDE/SATA/SAS/SCSI等块)、NAS(NFS、CIFS、SAMBA等文件系统)、SAN(FibreChannel, iSCSI, FoE存储网络块),Openfiler、FreeNas(ZFS快照复制)由于生产环境中往往由于对存储数据量很大,而SAN存储价格又比较昂贵,因此大多会选择分布式 存储来解决一下问题: 海量数据存储问题 数据高可用问题(冗余备份)问题 较高的读写性能和负载均衡问题 支持多平台多语言问题 高并发问题 主要对别指标 csdn这表格太难用了,我还是word整理后搬到这儿来的。 指标 适合类型 文件分布 系统性能 复杂度 FUSE POSIX 备份机制 通讯协议接口 社区支持 去重 开发语言 FastDFS 4KB~500MB 小文件合并存储不分片处理 很高 简单 不支持 不支持 组内冗余备份 Api HTTP 国内用户群 C语言 TFS 所有文件 小文件合并,以block组织分片 复杂 不支持 不支持 Block存储多份,主辅灾备 API http 少 C++ MFS 大于64K 分片存储 Master占内存多 支持 支持 多点备份动态冗余 使用fuse挂在 较多 Perl HDFS 大文件 大文件分片分块存储 简单 支持 支持 多副本 原生api 较多 java Ceph 对象文件块 OSD一主多从 复杂 支持 支持 多副本 原生api 较少 C++ MogileFS 海量小图片 高 复杂 可以支持 不支持 动态冗余 原生api 文档少 Perl ClusterFS 大文件 简单 支持 支持 镜像 多 C 二、常用的分布式文件系统 常见的分布式文件系统有FastDFS,GFS、HDFS、Ceph 、GridFS 、mogileFS、TFS等。各自适用于不同的领域。它们都不是系统级的分布式文件系统,而是应用级的分布式文件存储服务。 FastDFS介绍 ======================= 请参照FastDFS文件系统(一) fastdfs是什么? GFS(Google File System) ======================= Google公司为了满足本公司需求而开发的基于Linux的专有分布式文件系统。。尽管Google公布了该系统的一些技术细节,但Google并没有将该系统的软件部分作为开源软件发布。 下面分布式文件系统都是类 GFS的产品。 HDFS(Hadoop Distributed File System) ======================= Hadoop 实现了一个分布式文件系统,主要用于大数据计算存储,简称HDFS。 Hadoop是Apache Lucene创始人Doug Cutting开发的使用广泛的文本搜索库。它起源于Apache Nutch,后者是一个开源的网络搜索引擎,本身也是Luene项目的一部分。Aapche Hadoop架构是MapReduce算法的一种开源应用,是Google开创其帝国的重要基石。Ceph(https://github.com/ceph/ceph) ======================= 是加州大学圣克鲁兹分校的Sage weil攻读博士时开发的分布式文件系统。Ceph能够在维护 POSIX 兼容性的同时加入了复制和容错功能。Sage weil并使用Ceph完成了他的论文。说 ceph 性能最高,C++编写的代码,支持Fuse,并且没有单点故障依赖, 于是下载安装, 由于 ceph 使用 btrfs 文件系统, 而btrfs 文件系统需要 Linux 2.6.34 以上的内核才支持。 GridFS文件系统 ======================= MongoDB是一种知名的NoSql数据库,GridFS是MongoDB的一个内置功能,它提供一组文件操作的API以利用MongoDB存储文件,GridFS的基本原理是将文件保存在两个Collection中,一个保存文件索引,一个保存文件内容,文件内容按一定大小分成若干块,每一块存在一个Document中,这种方法不仅提供了文件存储,还提供了对文件相关的一些附加属性(比如MD5值,文件名等等)的存储。文件在GridFS中会按4MB为单位进行分块存储。 MogileFS ======================= 由memcahed的开发公司danga一款perl开发的产品,目前国内使用mogielFS的有图片托管网站yupoo等。 MogileFS是一套高效的文件自动备份组件,由Six Apart开发,广泛应用在包括LiveJournal等web2.0站点上。 MogileFS由3个部分组成: 第1个部分是server端,包括mogilefsd和mogstored两个程序。前者即是 mogilefsd的tracker,它将一些全局信息保存在数据库里,例如站点domain,class,host等。后者即是存储节点(store node),它其实是个HTTP Daemon,默认侦听在7500端口,接受客户端的文件备份请求。在安装完后,要运行mogadm工具将所有的store node注册到mogilefsd的数据库里,mogilefsd会对这些节点进行管理和监控。 第2个部分是utils(工具集),主要是MogileFS的一些管理工具,例如mogadm等。 第3个部分是客户端API,目前只有Perl API(MogileFS.pm)、PHP,用这个模块可以编写客户端程序,实现文件的备份管理功能。 TFS ------------------------------------- TFS(Taobao !FileSystem)是一个高可扩展、高可用、高性能、面向互联网服务的分布式文件系统,主要针对海量的非结构化数据,它构筑在普通的Linux机器 集群上,可为外部提供高可靠和高并发的存储访问。TFS为淘宝提供海量小文件存储,通常文件大小不超过1M,满足了淘宝对小文件存储的需求,被广泛地应用 在淘宝各项应用中。它采用了HA架构和平滑扩容,保证了整个文件系统的可用性和扩展性。同时扁平化的数据组织结构,可将文件名映射到文件的物理地址,简化 了文件的访问流程,一定程度上为TFS提供了良好的读写性能。 官网 : http://code.taobao.org/p/tfs/wiki/index/
一、FastDFS概述 FastDFS是阿里巴巴开源的一套轻量级,天生就是分布式设计的文件系统,FastDFS的源代码由C语言开发,目前可运行在Linux,FreeBSD,Unix等类操作系统上,FastDFS解决了大数据量文件存储(这里经常有人说成大数据,我本人是不赞同的)和读写分离,备份容错,负载均衡,动态扩容等问题,这也就是原作者所描述的高性能和高扩展性的文件系统。适合存储4KB~500MB之间的小文件,如图片网站、短视频网站、文档、app下载站等。 二、FastDFS作者简介 FastDFS的作者是余庆(happyfish100),github地址https://github.com/happyfish100 三、FastDFS主要特性 为互联网量身定制,海量数据文件存储。 高可用(同组备份机制)。 FastDFS不是通用的文件系统,只能通过api来访问,目前提供c,java,php客户端。phtyon由第三方开发者提供。 FastDFS可以看作是基于key/value pair存储系统,也许称为分布式文件存储服务更合适。 支持高并发(这个好像没体现出支持什么高并发,这个是nginx的功劳吧) 四、主要用户 京东(http://www.jd.com/),主要商品图片存储,可以看出来这是fastdfs典型路径 http://img12.360buyimg.com/n9/g15/M08/0B/19/rBEhWVMdbUMIAAAAAAEo7QHfEvoAAJwzAC7VvkAASkF751.jpg UC(http://www.uc.cn/),主要提供网盘服务 支付宝(https://www.alipay.com/), Lockbur高清壁纸分享网站(http://www.lockbur.com/),主要提供小图片存储服务。
转载地址 http://my.oschina.net/mycms/blog/525223 由于前端技术纷繁杂乱难以考核,为避免一叶障目,遂以此技术列表不拘一格降人才。 请客观地选择对应你当前知识&经验水平的选项,累加得分,如若得分超过100(最高可得分213+), 那你就是合格的前端工程师,恭喜你. (一) Developer Basic (24+) 1、热爱学习技术,热爱开源,热爱分享知识 (+5) 2、经常反思自己代码与技术上的不足并主动改善 (+8) 3、拥有一定程度的代码洁癖 (+8) 4、有好的代码提交、review习惯 (+3) 5、前端工作年限(+year*1) (二) Front-end Basic (45) 1、HTML5: 专家(+5) || 精通(+5) || 熟悉(+2) || 能用(-2) || 一窍不通(-10) 2、CSS2.1: 专家(+15) || 精通(+5) || 熟悉(+2) || 能用(-5) || 一窍不通(-100) 3、CSS3: 专家(+5) || 精通(+3) || 熟悉(+2) || 能用(-2) || 一窍不通(-10) 4、ES5: 专家(+15) || 精通(+5) || 熟悉(+2) || 能用(-10) || 一窍不通(-100) 5、ES6: 精通(+5) || 了解(+3) (三) Front-end Advanced (16) 1、CSS preprocessor: Stylus with nib(+4) || SCSS/SASS with compass(+4) || Less(-2) 2、Javascript preprocessor: CoffeeScript(+4) || TypeScript(+2) 3、SVG(+3) || Canvas(+3) 4、Web Responsive(+2) 5、Web Components(+2) || Polymer(+3) 6、异常精通浏览器兼容问题(-3),尤其擅长hack IE(-5) (四) Front-end Performance (10) 1、network performance(+3) 2、CSS performance(+3) 3、Javascript performance(+4) (五) Front-end Structure (7) 1、Yeoman(+2) 2、Gulp(+3) || Grunt(+3) 3、Bower(+2) (六) Front-end Support (8) 1、Function Programming: Underscore(+3) || Lodash(+3) || Lazy(+3) 2、DOM manipulation: jQuery(+5) (七) Front-end Module System (8) 1、AMD: RequireJS(+3) || SeaJS(+3) 2、CommonJS: Browserify(+2) || Duo(+2) 3、Other Module Bundler: Webpack(+3) (八) Front-end MVVM Framework & SPA (13) 1、AngularJS(+5) 2、Meteor(+3) 3、Ember(+3) 4、other MVVM frameworks or libraries(+2) (九) Front-end FRP(Functional Reactive Programing) (5) 1、React(+3) 2、Bacon(+2) || RxJS(+2) (十) Front-end Template Engine (4) 1、Handlebars.js(+2) 2、Jade(+2) || Ejs(+1) (十一) Front-end Responsive Framework (7) 1、Bootstrap3(+2) 2、Foundation4||5(+2) 3、Semantic-UI(+2) 4、other responsive framework(+1) (十二) Front-end Data Visualization (7) 1、Highcharts(+2) || Chart.js(+2) || epoch(+2) || other chart library(+2) 2、D3.js(+5) (十三) Game Engine (3) 1、Cocos2d-JS(+3) || others(+3) (十四) Basic Web Knowledge (12) 1、RestfulAPI&CORS(+3) 2、HTTP(+3) 3、Cache(+3) 4、XSS&CSRF(+3) (十五) Hybrid Mobile App (4) 1、Cordova.js(+2) || PhoneGap(+2) 2、Ionic Framework(+2) (十六) Node.js (11) 1、Express.js(+3) 2、Koa(+3) 3、Promise/Deferred(+3) 4、Stream(+2) (十七) Node.js Test Framework (4) 1、TDD: Mocha(+2) || Intern(+2) || other TDD library(+2) 2、BDD: Jasmine(+2) || Chai(+2) || other BDD library(+2) (十八) Backend Requirement (16+) 1、Any web language: Node.js, Python, Ruby, Go etc.(each +5) 2、NoSQL: MongoDB, Redis, RethinkDB etc.(+3) 3、Relational Database: MySql, Postgresql, oracle etc.(+5) 4、Nginx(+3) (十九) Tools (9) 1、Search Engine: Baidu(-2) || Google(+2) 2、Version Control: Git&Github(+2) 3、Platform: Mac OS X(+2) || Linux(+3) 4、Editor: (Vim||Emacs)(+2) || (Sublime||Textmate||other editor)(+1) || 5、IDE(-1) || DreamWeaver(-10) 最高可得分 = 213+ 若得分>=100,你就是高手。 如果超过150,你已经不是人了, 如果超过200,你已经不再需要做前端了, 如果你是230+,求你赶紧转行吧,有你再这行,所有妹子都没出头之日。 最后,我还整理部分+分 适用与后端开发人员和前端开发人员: 1.读过的相关技术书籍 2.感兴趣的社区 3.Github账号 4.Stack Overflow账号 5.个人Blog 6.可访问的作品 http://www.lockbur.com/ 其实写了这么多就是希望您能看见,http://www.lockbur.com/help/join7.回答技术问题 上面附带的地址果然是是好网站 业界良心.完全没有水印
一、使用sts (spring tools suite )开发maven项目,再执行run as -> maven install执行错误.错误主要提示: INFO] [INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ spring-boot-admin --- [INFO] Changes detected - recompiling the module! [INFO] Compiling 4 source files to C:\Users\Administrator\git\spring-boot-admin\target\classes [INFO] ------------------------------------------------------------- [ERROR] COMPILATION ERROR : [INFO] ------------------------------------------------------------- [ERROR] No compiler is provided in this environment. Perhaps you are running on a JRE rather than a JDK? [INFO] 1 error [INFO] ------------------------------------------------------------- [INFO] ------------------------------------------------------------------------ [INFO] BUILD FAILURE [INFO] ------------------------------------------------------------------------ [INFO] Total time: 5.811 s [INFO] Finished at: 2015-10-14T09:42:08+08:00 [INFO] Final Memory: 13M/204M [INFO] ------------------------------------------------------------------------ [ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile) on project spring-boot-admin: Compilation failure [ERROR] No compiler is provided in this environment. Perhaps you are running on a JRE rather than a JDK? [ERROR] -> [Help 1] [ERROR] [ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch. [ERROR] Re-run Maven using the -X switch to enable full debug logging. [ERROR] [ERROR] For more information about the errors and possible solutions, please read the following articles: [ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException 注意 [ERROR] No compiler is provided in this environment. Perhaps you are running on a JRE rather than a JDK? 这是因为sts安装默认是选择jre来运行程序的,而maven的的一些功能要求使用JDK。解决方法是配置sts的jre,修改成jdk。 修改后看起来是这样: 最后选中你当前项目 右键该项目 -> Maven -> Update Project
标题: red5 使用自带的直播程序案例延时问题处理 一 前置条件 1 red5服务器是启动中.. 2 再你webapps存在oflaDemo项目程序。 3 再你webapps下存在官方的demos项目程序. 4 打开两个浏览器。分别输入http://localhost:5080/demos/publisher.html 二 使用说明 1 打开其中一个浏览器(叫A浏览器)。因为你需要直播视频.所以先准备直播的视频流信息. a. 先点击Settings中connect按钮。确保能链接上服务器。 b. 然后点击顶部的publish按钮,切换到发布视频流操作界面。 c. 然后点击Publish按钮。服务端工作准备完毕 d. 另外说明一下.下面这个名称再播放直播时需要使用。 2 打开另外一个浏览器(叫B浏览器), 把buffer调整为0.然后再上面的Name粘贴到你新打开的页面中 Name的输入框中。点击play。就可以开始了.
1 问题出现 当我试着去https://github.com/Red5/red5-server下载Red5的源码,我是把自己编译源码的, 最终的Red5-server需要依赖其他几个组件: (1) red5-service (2) red5-server-common (3) red5-io 一次编译完上面三个项目之后,开始输入命令编译red5-server,因为这个才是我们最终想要的流媒体服务器. mvn -Dmaven.test.skip=true install 当让有上面这一步还不够:上面只是完成了编译工作.我们还需要把red5运行的文件大成压缩包。 mvn -Dmaven.test.skip=true clean package -P assemble 这样完成之后,你将会再red5-server的target目录看到如下内容: 2 我首先会把red5-server-1.0.6-SNAPSHOT-server.zip复制到我常用的工作目录中去.然后解压该压缩包。解压之后看起来应该是这样: 3 然后一起准备妥当,双击red5.bat ,会弹出一个新的黑色窗口.然后程序开始启动.但是当我启动过程中,意外的事情发生了.red5没有像我想象的那么顺利.却抛出如下异常信息: 2015-09-08 13:41:12,813 [Launcher:/oflaDemo] WARN o.s.w.c.s.XmlWebApplicationContext - Exception encountered during context initialization - cancelling refresh attempt org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'web.scope' defined in ServletContext resource [/WEB-INF/red5-web.xml]: Invocation of init method failed; nested exception is java.lang.NoSuchMethodError: org.red5.server.scope.Scope$ConcurrentScopeSet.keySet()Ljava/util/concurrent/ConcurrentHashMap$KeySetView; at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1574) ~[spring-beans-4.1.7.RELEASE.jar:4.1.7.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:539) ~[spring-beans-4.1.7.RELEASE.jar:4.1.7.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476) ~[spring-beans-4.1.7.RELEASE.jar:4.1.7.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303) ~[spring-beans-4.1.7.RELEASE.jar:4.1.7.RELEASE] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.1.7.RELEASE.jar:4.1.7.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299) ~[spring-beans-4.1.7.RELEASE.jar:4.1.7.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194) ~[spring-beans-4.1.7.RELEASE.jar:4.1.7.RELEASE] at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:755) ~[spring-beans-4.1.7.RELEASE.jar:4.1.7.RELEASE] at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:757) ~[spring-context-4.1.7.RELEASE.jar:4.1.7.RELEASE] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480) ~[spring-context-4.1.7.RELEASE.jar:4.1.7.RELEASE] at org.red5.server.tomcat.TomcatLoader$1.run(TomcatLoader.java:485) [tomcatplugin-1.9.jar:na] Caused by: java.lang.NoSuchMethodError: org.red5.server.scope.Scope$ConcurrentScopeSet.keySet()Ljava/util/concurrent/ConcurrentHashMap$KeySetView; at org.red5.server.scope.Scope$ConcurrentScopeSet.hasName(Scope.java:1411) ~[red5-server-common-1.0.6-SNAPSHOT.jar:na] at org.red5.server.scope.Scope.hasChildScope(Scope.java:819) ~[red5-server-common-1.0.6-SNAPSHOT.jar:na] at org.red5.server.scope.Scope.init(Scope.java:872) ~[red5-server-common-1.0.6-SNAPSHOT.jar:na] at org.red5.server.scope.WebScope.register(WebScope.java:225) ~[red5-server.jar:na] at org.red5.server.scope.WebScope.afterPropertiesSet(WebScope.java:111) ~[red5-server.jar:na] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1633) ~[spring-beans-4.1.7.RELEASE.jar:4.1.7.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1570) ~[spring-beans-4.1.7.RELEASE.jar:4.1.7.RELEASE] ... 10 common frames omitted 4 问题解决方法 导致问题代码: Caused by: java.lang.NoSuchMethodError: org.red5.server.scope.ScopeConcurrentScopeSet.keySet()Ljava/util/concurrent/ConcurrentHashMapKeySetView; 经过查阅资料最后发现是java版本的事,我的环境一直用的jdk7 原来keySet不适合java8以下版本,解决方法:将java版本跳到java8版本,或者直接重新安装一个java8的jdk .
GlassFish移植Tips 美丽的爪哇岛的博客地址: http://www.blogjava.net/askcuix/ 最近,公司的GlassFish移植项目基本告以段落,由于之前的代码严重依赖于Weblogic,给移植工作带来了很大的难度,很多实现方式在GlassFish中根本就没有对应的替代品。在经历了几个月的移植之后,竟让我对Weblogic开始产生好感了,作为一款商用的Application Server,Weblogic确实非常成熟,非常强大,提供了很多特性,以帮助提高程序的运行效率,但是太笨重了,访问Admin Console极慢;GlassFish作为一款开源的Application Server,非常适合开发者使用,速度很快,并且严格遵照J2EE的标准,以达到平台独立的特性,但是确实简陋了点,只提供了最标准的实现,并且还存在一些明显的BUG,社区不够活跃,文档、资源都很少,可能是现在SUN处于动乱期,连商业Support都很难联系到。下面是我在做移植工作时,随笔记下来的一些小经验,让其他的同学们少受一些折磨,少踩一些坑。 1、EJBClient与weblogic.jar冲突 若使用EJB Client访问GlassFish中的EJB,需将appserv-rt.jar、appserv-ext.jar、appserv-deployment-client.jar和javaee.jar加入到classpath中。若classpath中存在weblogic.jar,则可能会遇到错误: java.lang.NoSuchMethodError:org.omg.CosTransactions.OTSPolicy.value()S 将weblogic.jar从classpath中移除即可。 2、Transaction使用 使用Spring的JtaTransactionManager需要配置两个属性:JtaTransactionManager 和userTransactionName。对于GlassFish,JtaTransactionManager 为 java:appserver/TransactionManager , userTransactionName 为 java:comp/UserTransaction。只有Bean管理的SessionBean和MDB允许使用UserTransaction,Entity Bean只允许使用Container管理的transaction。如果Container管理的SessionBean或MDB使用了UserTransaction,则会出现错误:Lookup of java:comp/UserTransaction not allowedfor Container managed Transaction beans。 3、HTTPThread count 使用asadmin修改HTTP的thread count后,从Admin Console上访问,AdminServer的配置可生效,但Cluster不生效,检查domain.xml已改变,通过asadmin查询也已生效,应该是GlassFish页面展示的BUG。 4、EJB Timer的使用 在GlassFish中使用EJBTimer,需要有一个独立的XADataSource,和数据表EJB__TIMER__TBL,建表语句可在<server_path>/lib/install/databases中找到。对于developer模式,GlassFish默认使用内置的__TimerPool,不需要你手工创建datasource和表;对于cluster模式,Admin Server会默认使用__TimerPool,Cluster则需要单独配置。如果让Admin Server和Cluster同时使用手工创建的datasource,则可能导致Cluster配置中的timer datasource在server重启后丢失,TimerService会出现异常,这应该是GlassFish的BUG,目前的解决方案就是Admin Server用默认的Timer配置,Cluster用另外的配置。 5、ClassLoader优先加载 在weblogic中,可以通过配置prefer-application-packages来优先加载application中的类,在GlassFish中则没有对应的方式来控制加载顺序,一个典型的场景就是:项目中采用CXF作为webservice的实现,但GlassFish中默认使用了Metro的实现,由于Metro的jar包比application加载的早,就会导致CXF依赖的类库没有正常加载,而是使用了Metro的JAX-WS的实现。 6、CMP配置中的数据库表名区分大小写 CMP在GlassFish中需要配置sun-cmp-mappings.xml,该XML中的table-name是区分大小写的,Oracle中的表名默认是大写的,如果这里的table-name写成小写,就会报找不到表的错误,可以通过添加一个*.dbschema文件,对表名进行适配,以减少切换数据库时的修改操作。 7、GlassFish的部署结果不可靠 在使用asadmin部署EAR时,如果没有遇到极其严重的错误,部署一般都会返回成功,但这个结果并不可靠,你需要关注server.log,如果这里出现了错误,应用程序则可能没有真正部署成功,在运行时就会出现错误,所以要确保你的程序部署时,server.log中没有错误信息。 8、TLD路径 根据JSP2.1规范,tld文件不能存放在/WEB-INF/classes或者/WEB-INF/lib目录中,特别不能放在/WEB-INF/tags目录或子目录中,否则会出现错误: exception:org.apache.jasper.JasperException: PWC6180: Unable to initializeTldLocationsCache root cause:org.apache.jasper.JasperException: PWC6336: Illegal TLD path/WEB-INF/tags/fn.tld, must not start with “/WEB-INF/tags” 在Tomcat和Weblogic中不会出现该问题,GlassFish则严格遵照规范,可将tld文件放置在/WEB-INF/tld目录。 9、注册servlet listener 在web.xml中注册servlet的listener时,在<listener>中添加多个<listener-class>不会报错,但是只有最后一个<listener-class>生效,因此,要注册多个listener,需要添加多个<listener>。 10、Pass-by-reference Weblogic中的call-by-reference能够极大的提高本地接口调用的效率,在GlassFish中也有相应的替代,就是pass-by-reference,可以在sun-ejb-jar.xml中对某个EJB进行配置,也可以在sun-application.xml中配置,这样就可以对整个application中的EJB生效。 11、HTTP错误消息体 当HTTP的ErrorCode大于400,并且相应的消息体是空时,GlassFish会自动在返回的Response中添加错误信息,对于使用HttpClient操作时,就可能和我们期望的Response不同,该问题的解决办法:在往Response中写入内容后,调用response.getOutputStream().flush()或 response.flushBuffer();或者在web.xml中设置ErrorcCde对应的ErrorPage,ErrorPage可以是一个空内容的页面。 12、ServletRequest中inputStream的使用 InputStream有一个markSupported属性,如果该属性为true,则支持mark和reset,可以多次读取该流,反之则只能读取一次该输入流。一种情形就是:如果在Filter中读取了该InputStream,则不能在Servlet中再次读取。ServletRequest中的InputStream在不同的Server中有不同的实现,在Weblogic中markSupported就设为了true,在GlassFish中则为false。
标题:ejb3中的@Schedule中的persistent属性的深入探索 1 实验环境: 应用服务器:glassfish4.0 数据库服务器: MYSQL Target Server Version : 50614 开发工具: netbeans8.0 2 我思考的问题:当我开发好了一个@Schedule定时任务之后,我开始思考这样一种场景,如果我的应用服务器(glassfish4)是集群,那么我的定时任务会怎么执行,因为在你发布一个项目到glassfish集群中,每一台jvm中都会运行相同的程序。那定时器也必然存在多份。那么我的定时器组件会在同一时间点执行多次吗(执行在多个jvm中)? 如果不会,是什么机制控制定时器的统一性,如果会,那么我们又改怎么解决呢? 3 在编写一个定时器任务时,代码如下: @Schedule(second = "*/5", minute = "*", hour = "*", persistent = true,info = "我是定时器") public void doWork() { System.out.println("timer: " + System.currentTimeMillis()); }(1 ) persistent 值官方给出的说明是 Specifies whether the timer that is created is persistent.指定在创建该定时器是否需要持久化.(2 )时间格式的参数你可以参考的别的文章,我本文中主要介绍persistent这个值的情况,该值的默认值是true,也就说定时器的数据信息室会保存到数据库去的,那么他保存到那个数据去了呢? 其实如果你设置了persistent等于true或者不设置,定时器的数据信息都会保存到内存数据库中。 对应的物理路径如下:D:\AppServer\my-glassfish4\glassfish\domains\domain1\lib\databases 你可以尝试删除该文件夹的内容试一试,看看变动后的效果。 4 那么既然是可以保存到数据库,那么是否也可以保存到mysql数据库呢。而不是内存数据库呢,答案是可以的。 在mysql中配置GlassFish中使用EJBTimer,需要有一个独立的数据源,和数据表EJB__TIMER__TBL,建表语句可在[glassfish安装目录]\glassfish\lib\install\databases中找到。对于developer模式,GlassFish默认使用内置的__TimerPool,不需要你手工创建datasource和表;对于cluster模式,Admin Server会默认使用__TimerPool,Cluster则需要单独配置。如果让Admin Server和Cluster同时使用手工创建的datasource,则可能导致Cluster配置中的timer datasource在server重启后丢失,TimerService会出现异常,这应该是GlassFish的BUG,目前的解决方案就是Admin Server用默认的Timer配置,Cluster用另外的配置。 建表语句如下: CREATE TABLE EJB__TIMER__TBL ( `CREATIONTIMERAW` BIGINT NOT NULL, `BLOB` BLOB, `TIMERID` VARCHAR(255) NOT NULL, `CONTAINERID` BIGINT NOT NULL, `OWNERID` VARCHAR(255) NULL, `STATE` INTEGER NOT NULL, `PKHASHCODE` INTEGER NOT NULL, `INTERVALDURATION` BIGINT NOT NULL, `INITIALEXPIRATIONRAW` BIGINT NOT NULL, `LASTEXPIRATIONRAW` BIGINT NOT NULL, `SCHEDULE` VARCHAR(255) NULL, `APPLICATIONID` BIGINT NOT NULL, CONSTRAINT `PK_EJB__TIMER__TBL` PRIMARY KEY (`TIMERID`) ); 把该ejbtimer_mysql.sql放到数据中执行,然后配置一个数据库连接池,我这里就直接修改默认的数据连接池__TimerPool。 <jdbc-resource pool-name="__TimerPool" jndi-name="jdbc/__TimerPool" object-type="system-admin"></jdbc-resource> <jdbc-resource pool-name="DerbyPool" jndi-name="jdbc/__default" object-type="system-all"></jdbc-resource> <jdbc-connection-pool max-pool-size="100" datasource-classname="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" res-type="javax.sql.XADataSource" steady-pool-size="32" name="__TimerPool"> <property name="password" value="Passw0rd"></property> <property name="user" value="root"></property> <property name="url" value="jdbc:mysql://127.0.0.1:3306/test?characterEncoding=GBK"></property> <property name="URL" value="jdbc:mysql://localhost:3306/test?characterEncoding=GBK"></property> <property name="Password" value="Passw0rd"></property> <property name="User" value="root"></property> </jdbc-connection-pool> 建表语句是在test库中执行的,这样配置之后你的ejb定时器使用的就是你自定义的数据库了。然后重新启动glassfish服务器,并把你定时器任务程序发布到glassfish上去。若果发布中提示EJB__TIMER__TBL表找不到,那你应该是忘记执行建表sql了。好了当我定时应用程序启动后: 这是我的代码程序: /** * 测试定时器的持久化状态 * @author 845885222@qq.com */ @Singleton public class NewSessionBean { @Schedule(second = "*/5", minute = "*", hour = "*", persistent = true, info = "我是定时器") public void doWork() { System.out.println("timer: " + System.currentTimeMillis()); } @Schedule(second = "*/6", minute = "*", hour = "*", persistent = true, info = "doWork2") public void doWork2() { System.out.println("doWork2: " + System.currentTimeMillis()); } }控制台输出结果如下: 信息: Redirecting to /common/index.jsf 信息: Admin Console: Initializing Session Attributes... 信息: timer: 1433846450001 信息: doWork2: 1433846454001 观察我mysql数据test下的EJB__TIMER__TBL,结果如下 以后集群就不在担心定时器不同步的问题了。
CAS SSO 中设置默认语言为汉语(国际化) 1 闲来无聊学一下CAS单点登录的一个开源实现:但是觉得如果要用到自己项目中。页面是需要修改的.但首先设置了汉语,会对你的修改有很好的帮助.其实在我当前使用的版本中cas-server-4.0.0,注意我是自己下载源码(https://github.com/Jasig/cas)使用maven构建的. 2 参考资料: cas国际化 3 其实如果只是测试用完全不用那么麻烦,在你配置cas之后在首页你就能看到一大堆语言包设置如下,点击一下某个语言就切换了你当前的语言环境. 4 问题在我需要在cas启动时默认就设置为中文.其实看了一下源码,cas使用的spring 国际化配置,那么久很简单了.你只需要修改/WEB-INF/cas-servlet.xml文件的 <!-- Locale Resolver --> <bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver" p:defaultLocale="zh_CN" /> p:defaultLocale=“zh_CN"即可 修改之后如下: 注意:最后说明一下.如果你是源码编译安装.common-collectins 版本需要换成3.x以上,否则运行到tomcat上会提示错误
1 Clean jPlayer skin效果图如下: 2 代码我就写在博客里面了,直接给下载地址: http://download.csdn.net/detail/wk313753744/8759629 3 如果需要歌词显示请参考我另外一篇博文: http://blog.csdn.net/wk313753744/article/details/38758317 如果有问题:请联系:845885222@qq.com 注意 只收邮件
在该文档中,我将带领大家使用基于JAX-RS REST风格的实现Jersey来上传文件到服务器制定的文件夹,如果是图片并读取显示出该图片。 准备工作: 准备一个form表单,有两个字段,一个是type="file"和type="text",并且表单需要使用POST方式提交。注意改表单需要使用multipart/form-data。 该项目使用netbeans8.0和glassfish4.0开发和运行。并且使用maven管理该工程; 需要在您的C盘建立一个文件夹,用来存储上传的文件。如C:\Newsportal\article_images 开发环境: 1 创建工程 在你项目空白处右键-》点击新建项目 2 在创建的项目中选择maven-》点击右侧web应用程序 3 填写工程的名字和maven的组ID和包名 4 选择该项目的运行环境为服务器Glassfish server 5 最后点击完成 准备搭建jersey的运行环境: 1 配置maven需要依赖包,maven的pom文件依赖如下: <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.vi8</groupId> <artifactId>jerseyUploadDemo</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <name>jerseyUploadDemo</name> <description> jersey上传文件DMEO </description> <properties> <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <!-- Jersey --> <dependency> <groupId>org.glassfish.jersey.core</groupId> <artifactId>jersey-server</artifactId> <version>2.0</version> <type>jar</type> <scope>provided</scope> </dependency> <dependency> <groupId>org.glassfish.jersey.ext</groupId> <artifactId>jersey-mvc-jsp</artifactId> <version>2.0</version> <type>jar</type> <scope>provided</scope> </dependency> <dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-json-jackson</artifactId> <version>2.0</version> <type>jar</type> <scope>provided</scope> </dependency> <!-- 上传文件需要该依赖--> <dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-multipart</artifactId> <version>2.0</version> <scope>provided</scope> </dependency> <!-- 这个用于上传文件工具操作--> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.4</version> </dependency> <dependency> <groupId>javax</groupId> <artifactId>javaee-web-api</artifactId> <version>7.0</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>1.7</source> <target>1.7</target> <compilerArguments> <endorseddirs>${endorsed.dir}</endorseddirs> </compilerArguments> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>2.3</version> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>2.6</version> <executions> <execution> <phase>validate</phase> <goals> <goal>copy</goal> </goals> <configuration> <outputDirectory>${endorsed.dir}</outputDirectory> <silent>true</silent> <artifactItems> <artifactItem> <groupId>javax</groupId> <artifactId>javaee-endorsed-api</artifactId> <version>7.0</version> <type>jar</type> </artifactItem> </artifactItems> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>2 配置web.xml用以支持jersey,配置如下: <?xml version="1.0" encoding="UTF-8"?> <web-app version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"> <filter> <filter-name>JerseyFilter</filter-name> <filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class> <init-param> <param-name>javax.ws.rs.Application</param-name> <!--MyApplication.java jersey加载--> <param-value>com.vi8.upload.MyApplication</param-value> </init-param> <init-param> <param-name>jersey.config.servlet.filter.staticContentRegex</param-name> <param-value>/(img|css|js|font)/.*</param-value> </init-param> <init-param> <param-name>jersey.config.servlet.filter.forwardOn404</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>jersey.config.server.mvc.templateBasePath.jsp</param-name> <param-value>/WEB-INF/pages</param-value> </init-param> </filter> <filter-mapping> <filter-name>JerseyFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>3 编写上面web.xml用到的MyApplication.java 如下: package com.vi8.upload; import javax.ws.rs.ApplicationPath; import org.glassfish.jersey.jackson.JacksonFeature; import org.glassfish.jersey.media.multipart.MultiPartFeature; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.server.mvc.jsp.JspMvcFeature; /** * qq: 845885222@qq.com * * @author Administrator */ @ApplicationPath("/") public class MyApplication extends ResourceConfig { public MyApplication() { packages("com.vi8.upload.resources"); register(JspMvcFeature.class); register(JacksonFeature.class); register(MultiPartFeature.class); } }以上步骤基本就是jersey运行环境准备工作,接下开始讨论文件如何上传的。 jersey文件上传: 1 文件上传的Resource类,你可以理解是spring mvc中控制器。UploadImageResource.java清单代码 package com.vi8.upload.resources; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.util.Calendar; import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.http.HttpServletResponse; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import org.apache.commons.io.FileUtils; import org.glassfish.jersey.media.multipart.ContentDisposition; import org.glassfish.jersey.media.multipart.FormDataBodyPart; import org.glassfish.jersey.media.multipart.FormDataContentDisposition; import org.glassfish.jersey.media.multipart.FormDataMultiPart; import org.glassfish.jersey.media.multipart.FormDataParam; /** * qq : 845885222 * * @author Administrator */ @Path("upload") public class UploadImageResource { /** * Constants operating with images */ private static final String ARTICLE_IMAGES_PATH = "c:/Newsportal/article_images/"; private static final String JPG_CONTENT_TYPE = "image/jpeg"; private static final String PNG_CONTENT_TYPE = "image/png"; /** * 第一种方式上传 * * @param fileInputStream * @param disposition * @return */ @POST @Path("uploadimage1 ") @Consumes(MediaType.MULTIPART_FORM_DATA) public String uploadimage1(@FormDataParam("file") InputStream fileInputStream, @FormDataParam("file") FormDataContentDisposition disposition) { String imageName = Calendar.getInstance().getTimeInMillis() + disposition.getFileName(); File file = new File(ARTICLE_IMAGES_PATH + imageName); try { //使用common io的文件写入操作 FileUtils.copyInputStreamToFile(fileInputStream, file); //原来自己的文件写入操作 //saveFile(fileInputStream, file); } catch (IOException ex) { Logger.getLogger(UploadImageResource.class.getName()).log(Level.SEVERE, null, ex); } return "images/" + imageName; } /** * * * 第二种方式上传 使用FormDataMultiPart 获取表单数据 * * @param form * @param response * @return * @throws UnsupportedEncodingException */ @POST @Path("uploadimage2") @Consumes(MediaType.MULTIPART_FORM_DATA) @Produces(MediaType.APPLICATION_JSON) public String uploadimage2(FormDataMultiPart form, @Context HttpServletResponse response) throws UnsupportedEncodingException { //获取文件流 FormDataBodyPart filePart = form.getField("file"); //获取表单的其他数据 FormDataBodyPart usernamePart = form.getField("username"); //ContentDisposition headerOfFilePart = filePart.getContentDisposition(); //把表单内容转换成流 InputStream fileInputStream = filePart.getValueAs(InputStream.class); FormDataContentDisposition formDataContentDisposition = filePart.getFormDataContentDisposition(); String source = formDataContentDisposition.getFileName(); String result = new String(source.getBytes("ISO8859-1"), "UTF-8"); System.out.println("formDataContentDisposition.getFileName()result " + result); String filePath = ARTICLE_IMAGES_PATH + result; File file = new File(filePath); System.out.println("file " + file.getAbsolutePath()); try { //保存文件 FileUtils.copyInputStreamToFile(fileInputStream, file); // saveFile(fileInputStream, file); } catch (IOException ex) { Logger.getLogger(UploadImageResource.class.getName()).log(Level.SEVERE, null, ex); } System.out.println("" + "images/" + result); response.setCharacterEncoding("UTF-8"); return "images/" + result; } /** * * 不从web服务器去读图片,在磁盘某个目录的文件可以通过流的方式去获取 ,通过 response.getOutputStream()放回数据 * * @param imageName image-name * @param type extension of image * @param response {@link HttpServletResponse} * @throws IOException */ @GET @Path("/images/{name}.{type}") public void showImg(@PathParam("name") String imageName, @PathParam("type") String type, @Context HttpServletResponse response) throws IOException { System.out.println("showImg"); try (InputStream in = new FileInputStream(ARTICLE_IMAGES_PATH + imageName + "." + type)) { FileUtils.copyFile(new File(ARTICLE_IMAGES_PATH + imageName + "." + type), response.getOutputStream()); // FileCopyUtils.copy(in, response.getOutputStream()); } } // 保存文件信息到磁盘 private void saveFile(InputStream uploadedInputStream, File file) { System.out.println("------saveFile-----"); try { OutputStream outpuStream = new FileOutputStream(file); int read = 0; byte[] bytes = new byte[1024]; // outpuStream = new FileOutputStream(new File(serverLocation)); while ((read = uploadedInputStream.read(bytes)) != -1) { outpuStream.write(bytes, 0, read); } outpuStream.flush(); outpuStream.close(); } catch (IOException e) { e.printStackTrace(); } } } 2 当然要测试你也许还需要准备一个带有form表单的jsp文件 <form action="${pageContext.request.contextPath}/upload/uploadimage2" method="post" enctype="multipart/form-data"> <p> 文件 :<input type="file" name="file"/><br /> 用户名: <input type="text" name="username"/><br /> </p> <input type="submit" value="上传" /> </form> 3 测试输入http://localhost:8080/jerseyUploadDemo/ 结果如下 最后贴上测试代码 在测试代码中会上面列出的有点出入.主要是显示使用Viewable 实现: 1 代码托管在此: https://github.com/wangko27/jerseyUploadDemo 2 如果不用github你也可以来这儿下载 http://download.csdn.net/detail/wk313753744/8751943 3 当然如果下载链接失效,你还可以加入qq群: 438394076 讨论
glassfish4系统启动脚本,你就是使用如下方式启动: $ service glassfish start 启动脚本 启动glassfish4的文件实例 : set -e ASADMIN=/usr/local/glassfish4/bin/asadmin case "$1" in start) echo -n "Starting GlassFish server: glassfish" # Increase file descriptor limit: ulimit -n 32768 # Allow "memory overcommit": # (basically, this allows to run exec() calls from inside the # app, without the Unix fork() call physically hogging 2X # the amount of memory glassfish is already using) echo 1 > /proc/sys/vm/overcommit_memory #echo #echo "GLASSFISH IS UNDER MAINTENANCE;" #echo "PLEASE DO NOT USE service init script." #echo LANG=en_US.UTF-8; export LANG $ASADMIN start-domain domain1 echo "." ;; stop) echo -n "Stopping GlassFish server: glassfish" #echo #echo "GLASSFISH IS UNDER MAINTENANCE;" #echo "PLEASE DO NOT USE service init script." #echo $ASADMIN stop-domain domain1 echo "." ;; *) echo "Usage: /etc/init.d/glassfish {start|stop}" exit 1 esac exit 0
来源:美丽的爪哇岛 GlassFish由一个或多个domain组成,一个domain是一个管理域,每个domain和一个administration server(也称作Domain Administration server或者DAS)关联,并可包含0到多个standalone instance或者cluster。每个cluster有一个或多个同类型的instance,一个instance运行于一个单独的JVM中,domain中的instance可运行在不同的物理机器上。 下面介绍一下GlassFish中的组件构成: Server Instance 一个server instance运行在一个单独的JVM中,instance可分为两种:standalong和cluster。DAS就是一个standalong的instance,可以完全不依赖cluster而存在。对于cluster的instance,可以对应到weblogic中的managed server,我们也称作traffic server。 Administrative Domain Glassfish中可以创建多个domain,一个domain管理一组server instance,一个instance属于一个单独的domain,domain中的instance可运行在不同的物理机器中。每个domain都有一套自己的配置、日志文件和部署区域,改变其中一个domain的配置不会影响到其它的domain。 Domain Administration Server(DAS) 一个domain有一个DAS,DAS是一个特别的server instance,它可以:验证管理员、接收来自于管理工具的请求、与domain中的instance进行通讯。DAS通常叫做admin server或者default server,把它称作default server是因为它是一个管理操作的默认目标。DAS中有一个仓库保存domain的配置和部署的应用,如果DAS宕机了,不会影响其它instance的运行,但是不能对管理的配置进行修改了,这时你需要创建一个新的DAS去restore之前的配置。 Cluster 一个cluster表示一组server instance,cluster内可以共享相同的应用、资源和配置信息。cluster可以包含不同物理机器上的instance,通过DAS可以很容易的对多机上的cluster的生命周期进行管理。Cluster能够让你很容易的处理水平扩展、负载均衡和宕机保护等问题。因为cluster中所有的instance都拥有相同的资源和应用配置,当一个instance或机器宕了,load balancer可以发现失败,并将traffic从失败的instance上重定向到cluster中的其它可用的instance,并回复session的状态。 Cluster、domain和instance之间的关系如下: 一个domain可以有0到多个cluster。 一个cluster可以有一到多个server instance。 一个cluster属于一个单独的domain。 Node Agent Node agent运行在每个server instance的机器中,包括运行DAS的机器。node agent的作用是: 根据DAS的指示去启动和停止instance。 重启失败的instance。 提供失败的server的log,以帮助远程诊断。 同步每个server instance的本地配置仓库和DAS的中央仓库。 当创建了一个新的instance时,它会创建instance所需的目录,并同步DAS的中央仓库到instance的本地仓库。 当删除instance时执行适当的清理。 每个物理机器至少需要一个node agent,如果一台机器中的instance属于多个domain,则每个domain都需要一个node agent,这种方式不推荐。因为node agent要监控server instance,所以node agnet必须要一直运行。 理解了GlassFish中各个组件的作用,在企业级环境部署时,就可以比较容易的搭建相应的服务器架构,以使应用程序得到最优的运行方式。 整体架构图:图一 图二 配置Instance 图三 集群结构架构参考
NetBeans IDE 8.0 提供了现成的代码分析器和编辑器,可在其中使用最新的 Java 8 技术 -- Java SE 8、Java SE 嵌入式 8 和 Java ME 嵌入式 8。IDE 还具备一系列新的增强功能,这些功能进一步改进了对 Maven 以及带 PrimeFaces 的 Java EE 的支持;提供了面向 HTML5 的全新工具,尤其适用于 AngularJS;还有针对 PHP 和 C/C++ 支持的改进。 NetBeans IDE 8.0 提供了英语、巴西葡萄牙语、日语、俄语和简体中文版本。 最新可用的下载是 NetBeans IDE 8.0.1,这是对 NetBeans IDE 8.0 的更新,包括下列新特性: 支持 WildFly 服务器和 GlassFish 4.1 maven能够支持pom图形依赖 对 JavaScript 重构的改进 HTML5 项目改进(Grunt、Karma、Bower) 集成最近的补丁程序以及更多内容 文档 使用以下主题可了解有关 NetBeans IDE 8.0 的入门知识: 安装说明 截屏视频和教程 发行说明 新增功能集锦 Java JDK 8 支持:增强了工具和编辑器在使用配置文件、Lambdas 和流方面的功能。 Java SE 嵌入式支持,可以直接从 NetBeans IDE 在嵌入式设备(例如 Raspberry PI)上部署、运行、调试或分析 Java SE 应用程序。 Java ME 嵌入式 8 支持。 多个 Java 编辑器增强功能,例如许多新的 Java 提示、显示为工具提示的 Javadoc、即时重命名增强功能以及代码完成排除。 改进了与 JavaFX 场景构建器的集成。 完整屏幕快照 Java EE 新的 PrimeFaces 代码生成器,可以在连接到数据库时生成完整的 PrimeFaces 框架 CRUD 应用程序。 支持 WildFly 服务器和 WebLogic 12.1.3 捆绑的 GlassFish 已更新为 4.1 Tomcat 8.0.9 和 TomEE 支持,包括现成的 JPA、JSF 和 JAXRS 配置及数据源。 Facelets 模板客户端向导负责生成段。 模板段的代码完成,以及 JSF 复合组件中的代码完成。 beans.xml 文件中的增强 CDI 集成,带有用于替代类和构造型的代码完成。 更加灵活的 JPA 提示,可以禁用和启用该提示并更改警告级别。 完整屏幕快照 Maven 新的 Maven 图形布局切换器。 导航器显示 POM 目标。 显示在“项目”窗口中的附加 Web 资源。 改进了重新运行 Web 应用程序时的 Maven 性能。 改进了调试期间“应用代码更改”的 Maven 性能。 完整屏幕快照 JavaScript RequireJS 支持 通过从视图到控制器的超链接来进行 AngularJS 导航。 针对工件之间代码完成中的 AngularJS 提供了许多增强功能,例如在视图中可以实现控制器中定义的属性的代码完成。 Knockout 模板中的代码完成支持。 针对创建 JQuery 窗口部件和插件的新编辑器支持。 支持在 Nashorn (JDK 8+) 脚本引擎中执行的 JavaScript 代码调试。 完整屏幕快照 HTML5 支持 Cordova 3.3+ 的 Android 4.4 WebKit 调试。 新的 Karma 测试运行器支持。 新的 Grunt 构建支持。 Avatar.js 可从插件管理器通过“工具”|“插件”获得。在安装后,您能够创建 Avatar.js 项目。 完整屏幕快照 PHP 支持 PHP 5.6。 新的 Nette Tester 支持。 新的 PHP CS 修复程序支持。 Twig、Latte 和 Neon 的增强功能。 “新建项目”向导支持编写器。 完整屏幕快照 C/C++ 提供在编辑器中导航的新面包屑功能。 新的 GDB 控制台,可以启用命令行 GDB 模式。 新的提示和代码完成。 改进了远程性能。 完整屏幕快照 平台 提高了窗口系统的灵活性,例如通过拖放操作拆分编辑器,并且提供了新的 API 来定制编辑器选项卡。 用于管理项目组的新用户界面设计。 文件夹中的文件可以按扩展名排序。 完整屏幕快照 其他 分析器增强功能:分析嵌入式平台、显示锁争用的线程属主监视器,以及改进 "Threads"(线程)视图。 版本控制增强功能:支持 Subversion 1.8 工作副本,各种 Git 和 Mercurial 支持增强功能。 任务计划以及 "Tasks"(任务)窗口中用于错误跟踪的其他新功能。 完整屏幕快照 有关此发行版本功能的详细信息,请参见 NetBeans IDE 8.0.1 新增功能和重要功能页。 另请参阅“NetBeans IDE 8.0 新增功能和重要功能”页面。 请访问 NetBeans 发行计划图。
如何使用commons.lang3生成随机的20位的订单号: 第一步:引入maven依赖或者去官网下载相应的jar包下载地址:http://apache.fayea.com//commons/lang/binaries/commons-lang3-3.3.2-bin.zip <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.3.2</version> </dependency> 第二步 如何使用这里注意使用的RandomStringUtils类: public static void main(String[] args) { //这参数20代表你生成20位随机串,当然你也可以设置为30,40等, String random = RandomStringUtils.randomNumeric(20); System.out.println("random:" + random); //########################输出结果########################### //--- exec-maven-plugin:1.2.1:exec (default-cli) @ LogginInject --- //random:39774068977122755483 //########################输出结果########################### } 第三步 编写一个完整的获取订单类: package com.veight.logging; import org.apache.commons.lang3.RandomStringUtils; /** * 作者 :845885222@qq.com * * @订单号生成工具 * * @author youyou */ public class OrderGenerator { private static final int ORDER_DEFAULT_LENGTH = 20; public static String order() { return order(ORDER_DEFAULT_LENGTH); } //获取一个指定长度的订单号 private static String order(int length) { return RandomStringUtils.randomNumeric(length); } } 第四步 订单工具类使用: public static void main(String[] args) { String oderId = OrderGenerator.order(); String oderIdBylength = OrderGenerator.order(20); System.out.println("oderId:" + oderId); System.out.println("oderIdBylength:" + oderIdBylength); } ok 恭喜你
Jboss7中ejb3使用@Schedule调度器总是每分钟执行 1 问题:当我尝试着去开发一个ejb3的@Schedule调度器来执行我预定每5秒钟执行一次的任务时。我意外的发现无论我怎么设置,该@Schedule以每分钟来运行。 import java.text.SimpleDateFormat; import java.util.Date; import javax.ejb.Schedule; import javax.ejb.Stateless; /** * 845885222@qq.com * @author youyou */ @Stateless public class MakeHtmlSchedule { @Schedule(second = "*/10", minute = "*", hour = "*", persistent = false) public void doWork() { Date currentTime = new Date(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy.MM.dd G 'at' HH:mm:ss z"); System.out.println("ScheduleExample.doWork() invoked at " + simpleDateFormat.format(currentTime)); } } 2 一个定时调度器不一定是@singleton注解的对象,也可以使用@Stateless,甚至使用@Stateful的bean。如果使用的@Stateless或者@Stateful,那么doWork()方法只会是每分钟调用一次。改动为使用@Singleton装饰,就可以实现每10秒运行一次doWork()方法了。 import java.text.SimpleDateFormat; import java.util.Date; import javax.ejb.Schedule; import javax.ejb.Singleton; /** * 845885222@qq.com * @author youyou */ @Singleton public class MakeHtmlSchedule { @Schedule(second = "*/10", minute = "*", hour = "*", persistent = false) public void doWork() { Date currentTime = new Date(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy.MM.dd G 'at' HH:mm:ss z"); System.out.println("ScheduleExample.doWork() invoked at " + simpleDateFormat.format(currentTime)); } } 每10秒钟执行一次的效果: VEIGHT 2015-01-31 22:59:00,002 INFO (EJB default - 5) ScheduleExample.doWork() invoked at 2015.01.31 公元 at 22:59:00 CST VEIGHT 2015-01-31 22:59:10,002 INFO (EJB default - 7) ScheduleExample.doWork() invoked at 2015.01.31 公元 at 22:59:10 CST VEIGHT 2015-01-31 22:59:20,001 INFO (EJB default - 8) ScheduleExample.doWork() invoked at 2015.01.31 公元 at 22:59:20 CST 3 参考链接:http://www.adam-bien.com/roller/abien/entry/simplest_possible_ejb_3_16
1 关于hibernate的@Formula用法和作用可以参照别的文章 2 hibernate提取属性也叫做计算属性,该属性值是一个值读属性,是通过使用sql语句获取得到的,常用的是统计数据 3 案例:我有一个employee实体类,该类有主键Id或name,月薪水(monthlySalary)等属性(字段),你现在有一个想法想要获取该employee的年薪,计算方式是月薪*12个月=年薪。 package net.ozar.exp.entity; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name="EMPLOYEE") public class Employee implements java.io.Serializable { private static final long serialVersionUID = -7311873726885796936L; @Id @Column(name="ID") private Integer id; @Column(name="FIRST_NAME", length=31) private String firstName; @Column(name="LAST_NAME", length=31) private String lastName; @Column(name="MONTHLY_SALARY") private float monthlySalary; public Employee() { } // getters and setters // ... public float getMonthlySalary() { return monthlySalary; } public void setMonthlySalary(float monthlySalary) { this.monthlySalary = monthlySalary; } /* This artificial property - as I call it - is a kind of a calculated property, but not with Hibernate derived property support - not just yet */ public float getYearlySalary() { return this.monthlySalary * 12; } }4 上面这个是没有加入hibernate @@Formula支持的用法,现在我们加入这个注解实现: package net.ozar.exp.entity; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import org.hibernate.annotations.Formula; @Entity @Table(name="EMPLOYEE") public class Employee implements java.io.Serializable { private static final long serialVersionUID = -7311873726885796936L; @Id @Column(name="ID") private Integer id; @Column(name="FIRST_NAME", length=31) private String firstName; @Column(name="LAST_NAME", length=31) private String lastName; @Column(name="MONTHLY_SALARY") private float monthlySalary; @Formula("MONTHLY_SALARY*12") private float yearlySalary; public Employee() { } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public float getMonthlySalary() { return monthlySalary; } public void setMonthlySalary(float monthlySalary) { this.monthlySalary = monthlySalary; } public float getYearlySalary() { return yearlySalary; } }5 注意到上面这个案例的@Formla的值是关联到“MONTHLY_SALARY”而yearlySalry属性是没有存储如数据库的。 6 一些更为复杂用法 @Formula("(select min(l.creation_date) from Logs l where l.customer_id = id)") private Date firstLoginDate;注意:最後面的customer_id=id中后面的id的值也就当前对象的id值
1 在spring项目中Spring tool suite是一个很好开发工具。 2 那我在添加Server后,我本次笔记中使用的tomcat7,在sts中默认的发布目录是.metadata/.plugins/org.eclipse.wst.server.core/tmp0。 3 有的时候我们需要使用tomcat server的的配置文件。我们也许希望把项目发布到我D:\AppServer\apache-tomcat-7.0.56\webapps目录下。 4 点击查看Servers视图。如下(图 01): (图 01) 5 双击 Tomcat v7.0 Server at localhost 之后就可以看下如下(图 02)视图: (图 02) 6 然后重新启动的tomcat 7的应用服务器。好 恭喜你。
1 ubuntu的iso下载地址也是最近才找到的: http://ftp.yzu.edu.tw/Linux/ubuntu-cdimage/ubuntu/ 2 linuxmint的iso镜像下载地址: http://ftp.yzu.edu.tw/Linux/linuxmint/isos/stable/
1 在jersey中的代码如下: @GET @Path("getCarInsuranceList") @Produces(MediaType.APPLICATION_JSON) public List<CarInsurance> getCarInsuranceList(@FormParam("startDate") String startDate, @FormParam("endDate") String endDate, @FormParam("carInsuranceStatus") CarInsuranceStatus carInsuranceStatus) { List<CarInsurance> requests = carInsuranceDao.listCarInsurance(DateUtils.FIRST_DATE, new Date(), PageInfo.ALL, carInsuranceStatus).getResults(); return requests; } 这样一般来说肯定在服务启动时候就会报错的: CarInsuranceStatus), should not consume any form parameter.; 2 解决方法 如果注解使用的@GET标注的,则使用@QueryParam, 如果注解使用的@POST标注的,则使用@FormParam, 3 如下改正后的代码: @GET @Path("getCarInsuranceList") @Produces(MediaType.APPLICATION_JSON) public List<CarInsurance> getCarInsuranceList(@QueryParam("startDate") String startDate, @QueryParam("endDate") String endDate, @QueryParam("carInsuranceStatus") CarInsuranceStatus carInsuranceStatus) { List<CarInsurance> requests = carInsuranceDao.listCarInsurance(DateUtils.FIRST_DATE, new Date(), PageInfo.ALL, carInsuranceStatus).getResults(); return requests; } 4 参考别人说法地址:http://stackoverflow.com/questions/17125747/jersey-should-not-consume-any-form-parameter-exception
1 经验之谈:我在ace(一个基于bootstrap)的后台模板框架中,需要使用弹出确认框,看了一下ace中,自己带有弹框功能,所以就拿来就用。 2 问题:就是在我使用过程中,我发现一个不好的现象,默认是英文的,不是太友好。所以我想改成按钮和提示信息都是中文,好在已经提供中文支持,所以只需要设置一下就可以使用了。 代码如下: $("#paymentComplete").click(function() { bootbox.setLocale("zh_CN"); bootbox.confirm("确定把修改该条记录状态为缴费完成?", function(result) { if (result) { // } }); }); 3 补充说明我的bootbox.js是 * bootbox.js v3.3.0,所以你如果使用的4.0是不一样的。 相关连接地址:http://bootboxjs.com/ 4 最终效果图如下:没点击之前 点击效果:
Ejb中的@Local和@LocalBean的区别 1 、在ejb3.1之后引入了@LocalBean的注解,我们之前(ejb3.0)使用的@Local,在javax.ejb包下,这个包是javaEE 7api的,在java7 seapi中是找不到的,2 、@LocalBean的用途:当你开发一个ejb会话Bean没有实现任何接口,但你又想暴露给其他对象访问的时候,那么你就可以使用@LocalBean,当然你如果不喜欢写很多接口的话,你也可以使用这种方式代替你原有的@Local标注的接口,然后在写一个实现类的那种模式。 3 、 那我们怎么用@LocalBea呢?给出一个代码案列如下: @Singleton @LocalBean public class MyNoInterfaceBean { public void sample() { } } 4 、和使用原来的@Local方式对比一下: @Local public interface LocalView { public void sample(); } @Singleton public class MyLocalBean implements LocalView { public void sample() { } } 5 、在调用都用@EJB注解,如: 1 种使用@LocalBean方式 @EJB Private MyNoInterfaceBean myNoInterfaceBean; 2 种使用@Local方式 @EJB Private LocalView localView;
来源:http://www.forbeschina.com/review/201308/0027889.shtml 作者:刘润 8月23日,微软CEO史蒂夫·鲍尔默宣布将在未来12个月内退休。微软股价随即暴涨。 如果是你,受全球最伟大的企业家之一比尔盖兹托孤,掌舵微软,然后发誓“鞠躬尽瘁,死而后已”,以“青山处处埋忠骨,一腔热血洒高原”的激情,发誓要把微软带回最顶峰时代,却没想,屡战屡败,屡败屡战,股价一直微微有点软。如果是你,作为全球最知名的CEO之一,终于决定挂印封金,弃甲归田,把失地交给后人去攻打,公司股票却跟打了鸡血一样,盘前暴涨8%,你会作何感想? 我想,老鲍写完引退公开信,坐在电脑前,看着微软市值因为自己离开而暴涨的200亿美元,心里一定在想:难道,我这十几年做的最正确的事情,就是什么都不做,转身离开?我这十几年的努力,最有用的,原来是这一封信!这个讽刺,太无情了一些,让人于心不忍。 如果仅仅从销售业绩、利润指标来看,鲍尔默有点冤,他执掌期间,微软业绩其实并不是那么差。我在微软的近14年,公司的营业收入(revenue)几乎每年都以两位数字的速度增长,微软的净利润率(net profit margin)至今高于苹果和谷歌。 这个故事是这样的。 感谢早期的探险者比尔盖兹,包括鲍尔默,发现一座“金银岛”,并找到了Windows和Office这两座世界上最大的金矿和银矿,垄断了几乎全世界的金银供应。从2000年开始接任岛主的鲍尔默,不但继续开采着宝藏,还成功地击退了几乎所有的虎视眈眈者,让全岛的人过着幸福的生活。微软至今仍然是全世界疯狂的印钞机之一。 但是,人们渐渐发现,金山银山,也有吃光用光的一天。同时,左边有另一座原来不知名的小岛,叫谷歌,那里的人居然开出一座闻所未闻,叫做“网络广告”的新矿,一夜暴富;右边一座曾经辉煌但早已废弃的旧岛,叫苹果,也再次发现一产量巨大的“移动设备”矿,重振旗鼓。金银岛矿产日益枯竭,人们对金银的兴趣也逐渐下降。所有人把目光转向鲍尔默:你是岛主,请带领微软,找到一座产量不比金银岛小,能赚更多钱的新矿吧!我们不但要采旧矿,更要开新矿。 可是这谈何容易。《创新者的窘境》(The Innovator's Dilemma)给大企业归纳了一个“失败框架”: 1)你很难通过改善“延续性技术”打败大企业,只有“破坏性技术”才能通过截然不同的价值主张,颠覆大企业的商业模式根基;2)延续性技术一定会发展到“过度满足”市场的需求,从而饱和,发展停滞;3)但是同时,“破坏性技术”初期并不“诱人”,价格便宜、利润率低,也不被大企业的主要客户接受,所以很难被大企业投资。 所以,大企业的失败,几乎是注定的,这是自然规律。失败未必会消亡,可能会偏隅一角,如手表的奢侈品化,胶卷的发烧友化,纸媒的小众化。没有一个公司的领导者可以以一己之力捍卫整个即将被颠覆的行业。成功的领导者,一定是带领公司离开这个行业,寻找新的宝藏。哪里才有新的、足够大的宝藏,这对大企业领导者来说,无异于一场赌博。IBM赌对了,新生为第二个IBM,苹果赌对了,新生为第二个苹果。微软,至今还是最一开始那个微软。 所以,鲍尔默并没有“导致”微软失败,鲍尔默是在不断尝试拯救“微软一代”于自然规律定义的必然失败,但是尚未成功。 顺着这条脉络,我们再来看看微软这些年的发展。 鲍尔默的第一次试着让微软重生是在刚掌印没多久时,2000年左右,宣布微软不再是一家软件公司,将要成为一家服务公司。鲍尔默把软件时代的公司使命“让每个人的桌面上都有一台电脑”改为服务时代的“让全世界的人和企业都充分认识到自己的潜力”。鲍尔默没有赌对,至少股价上看是这样。投资人的抱怨开始增多。于是,2012年,鲍尔默再次修改了公司的使命为“为每个人,每台设备,每种业务,提供不间断的云服务”,宣布公司进入“设备+服务”时代,并开始不断重组,调整公司的资源配置。 这次,鲍尔默赌对了吗? 现在还不到盖棺定论的时候,但是显然有些人失去了耐心。外界呼吁鲍尔默下台的声音越来越大,不少人认为鲍尔默赌性很强,但总下错注。甚至在一些内部会议,微软员工也开始公开提问:请问您打算什么时候离开微软?有次我在现场,认真地观察着他的表情。我觉得他突然有点像孩子一样害羞起来,说:你知道,我没有什么爱好,我唯一的爱好,就是工作。除了工作,我不知道干什么好。所以,我不会离开,我会一直在微软工作下去,我爱我的工作。 没想到,几个月后,鲍尔默宣布即将离职。我脑海中只有四个字:愿赌服输。 鲍尔默做错了什么?他没有赌对下一个“破坏性技术”。一代枭雄,只论胜负,上一轮押错了,所以现在必须离场。不过在走之前,他对第二轮重注还是非常有信心,并在离职邮件里不断重复其正确性,希望未来领导人可以遵守。这次,鲍尔默真的会赌对吗?这只能留给下一任CEO以后判断了。 现在,作为一名在微软工作了近14年的退伍老兵,我祝愿微软有更好的发展,也祝福鲍尔默退休以后,可以找到工作以外的新乐趣。
1、该插件是一个jquery的编写的跟jplayer实现歌词同步的插件,最终效果如图: 2、首先引入jplayer的相关的js库和样式文件。 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <link href="skin/pink.flag/jplayer.pink.flag.css" rel="stylesheet" type="text/css" /> <script type="text/javascript" src="js/jquery-1.8.2.min.js"></script> <script type="text/javascript" src="js/jquery.jplayer.min.js"></script> <script type="text/javascript" src="js/jquery.jplayer.lyric.js"></script> 3、把我修改的jquery的js文件贴出来,以备以后不能下载的朋友参考实现: /***** *原作mp3player.duapp.com *修改 :王坤 *QQ群:151013733 *修改内容说明:在原有基础上,需要使用start方法才能加载歌词,改进之后,在加载jplayer之前调用,显示歌词 */ (function($){ $.lrc = { handle: null, /* 定时执行句柄 */ list: [], /* lrc歌词及时间轴数组 */ regex: /^[^\[]*((?:\s*\[\d+\:\d+(?:\.\d+)?\])+)([\s\S]*)$/, /* 提取歌词内容行 */ regex_time: /\[(\d+)\:((?:\d+)(?:\.\d+)?)\]/g, /* 提取歌词时间轴 */ regex_trim: /^\s+|\s+$/, /* 过滤两边空格 */ callback: null, /* 定时获取歌曲执行时间回调函数 */ interval: 0.3, /* 定时刷新时间,单位:秒 */ format: '<li>{html}</li>', /* 模板 */ prefixid: 'lrc', /* 容器ID */ hoverClass: 'hover', /* 选中节点的className */ hoverTop: 100, /* 当前歌词距离父节点的高度 */ duration: 0, /* 歌曲回调函数设置的进度时间 */ __duration: -1, /* 当前歌曲进度时间 */ hasLrc:0,/**记录是否有歌词标记**/ //初始化歌词 init: function(txt){ if(typeof(txt) != 'string' || txt.length < 1) return; /* 停止前面执行的歌曲 */ this.stop(); var item = null, item_time = null, html = ''; /* 分析歌词的时间轴和内容 */ //先按行拆分歌词 txt = txt.split("\n"); //对拆分的每行进行提取时间和歌词内容 for(var i = 0; i < txt.length; i++) { //获取一行并去掉两端的空格 [00:11.38]如果你眼神能够为我片刻的降临 item = txt[i].replace(this.regex_trim, ''); //然后取出歌词信息 if(item.length < 1 || !(item = this.regex.exec(item))) continue; while(item_time = this.regex_time.exec(item[1])) { this.list.push([parseFloat(item_time[1])*60+parseFloat(item_time[2]), item[2]]); } this.regex_time.lastIndex = 0; } /* 有效歌词 */ if(this.list.length > 0) { this.hasLrc =1; /* 对时间轴排序 */ this.list.sort(function(a,b){ return a[0]-b[0]; }); if(this.list[0][0] >= 0.1) this.list.unshift([this.list[0][0]-0.1, '']); this.list.push([this.list[this.list.length-1][0]+1, '']); for(var i = 0; i < this.list.length; i++) html += this.format.replace(/\{html\}/gi, this.list[i][1]); /* 赋值到指定容器 */ $('#'+this.prefixid+'_list').html(html).animate({ marginTop: 0 }, 100).show(); /* 隐藏没有歌词的层 */ $('#'+this.prefixid+'_nofound').hide(); /* 定时调用回调函数,监听歌曲进度 */ //this.handle = setInterval('$.lrc.jump($.lrc.callback());', this.interval*1000); }else{ /* 没有歌词 */ this.hasLrc =0; $('#'+this.prefixid+'_list').hide(); $('#'+this.prefixid+'_nofound').show(); } }, /* 歌词开始自动匹配 跟时间轴对应 */ /**callback时间 jplayer的当前播放时间**/ start: function(callback) { this.callback = callback; /* 有歌词则跳转到歌词时间轴 */ if(this.hasLrc == 1) { this.handle = setInterval('$.lrc.jump($.lrc.callback());', this.interval*1000); } }, /* 跳到指定时间的歌词 */ jump: function(duration) { if(typeof(this.handle) != 'number' || typeof(duration) != 'number' || !$.isArray(this.list) || this.list.length < 1) return this.stop(); if(duration < 0) duration = 0; if(this.__duration == duration) return; duration += 0.2; this.__duration = duration; duration += this.interval; var left = 0, right = this.list.length-1, last = right pivot = Math.floor(right/2), tmpobj = null, tmp = 0, thisobj = this; /* 二分查找 */ while(left <= pivot && pivot <= right) { if(this.list[pivot][0] <= duration && (pivot == right || duration < this.list[pivot+1][0])) { //if(pivot == right) this.stop(); break; }else if( this.list[pivot][0] > duration ) { /* left */ right = pivot; }else{ /* right */ left = pivot; } tmp = left + Math.floor((right - left)/2); if(tmp == pivot) break; pivot = tmp; } if(pivot == this.pivot) return; this.pivot = pivot; tmpobj = $('#'+this.prefixid+'_list').children().removeClass(this.hoverClass).eq(pivot).addClass(thisobj.hoverClass); tmp = tmpobj.next().offset().top-tmpobj.parent().offset().top - this.hoverTop; tmp = tmp > 0 ? tmp * -1 : 0; this.animata(tmpobj.parent()[0]).animate({marginTop: tmp + 'px'}, this.interval*1000); }, /* 停止执行歌曲 */ stop: function() { if(typeof(this.handle) == 'number') clearInterval(this.handle); this.handle = this.callback = null; this.__duration = -1; this.regex_time.lastIndex = 0; this.list = []; }, animata: function(elem) { var f = j = 0, callback, _this={}, tween = function(t,b,c,d){ return -c*(t/=d)*(t-2) + b; } _this.execution = function(key, val, t) { var s = (new Date()).getTime(), d = t || 500, b = parseInt(elem.style[key]) || 0, c = val-b; (function(){ var t = (new Date()).getTime() - s; if(t>d){ t=d; elem.style[key] = tween(t,b,c,d) + 'px'; ++f == j && callback && callback.apply(elem); return true; } elem.style[key] = tween(t,b,c,d)+'px'; setTimeout(arguments.callee, 10); })(); } _this.animate = function(sty, t, fn){ callback = fn; for(var i in sty){ j++; _this.execution(i,parseInt(sty[i]),t); } } return _this; } }; })(jQuery); 4、在jplayer初始化中使用如下: $(document).ready(function(){ $("#jquery_jplayer_1").jPlayer({ ready: function () { //初始化歌词信息(传入歌词文件文本) $.lrc.init($('#lrc_content').val()); $(this).jPlayer("setMedia", { title: "Bubble", mp3: "mp3/林俊杰 - 曹操.mp3" }).jPlayer("play"); }, timeupdate: function(event) { if(event.jPlayer.status.currentTime==0){ time = ""; }else { time = event.jPlayer.status.currentTime; } }, play: function(event) { //点击开始方法调用lrc.start歌词方法 返回时间time if($('#lrc_content').val()!==""){ $.lrc.start(function(){ return time; }); }else{ $(".content").html("没有字幕"); } }, swfPath: "js", supplied: "mp3", wmode: "window", smoothPlayBar: true, keyEnabled: true, remainingDuration: true, toggleDuration: true }); }); 5、这一步不是必要的 只是提供一个我的源码给你们参考: <!DOCTYPE HTML> <html> <head> <title>QQ群:845885222 完整jplayer歌词同步demo</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <link href="skin/pink.flag/jplayer.pink.flag.css" rel="stylesheet" type="text/css" /> <script type="text/javascript" src="js/jquery-1.8.2.min.js"></script> <script type="text/javascript" src="js/jquery.jplayer.min.js"></script> <script type="text/javascript" src="js/jquery.jplayer.lyric.js"></script> <style type="text/css"> *{ margin:0; padding:0; } ul, ol, dl { list-style:none; } .content li.hover{color:red; } .content{ width:200px;overflow:hidden;padding:10px; text-align: center; font-size:12px;} </style> <script type="text/javascript"> //<![CDATA[ $(document).ready(function(){ $("#jquery_jplayer_1").jPlayer({ ready: function () { //初始化歌词信息(传入歌词文件文本) $.lrc.init($('#lrc_content').val()); $(this).jPlayer("setMedia", { title: "Bubble", mp3: "mp3/林俊杰 - 曹操.mp3" }).jPlayer("play"); }, timeupdate: function(event) { if(event.jPlayer.status.currentTime==0){ time = ""; }else { time = event.jPlayer.status.currentTime; } }, play: function(event) { //点击开始方法调用lrc.start歌词方法 返回时间time if($('#lrc_content').val()!==""){ $.lrc.start(function(){ return time; }); }else{ $(".content").html("没有字幕"); } }, swfPath: "js", supplied: "mp3", wmode: "window", smoothPlayBar: true, keyEnabled: true, remainingDuration: true, toggleDuration: true }); }); //]]> </script> </head> <body> <!--textarea只是用来存储歌词信息 并不做显示,如果要修改显示样式,修改id="lrc_list"的样式就行--> <textarea id="lrc_content" style="display:none;"> [ti:曹操] [ar:林俊杰] [al:曹操] [00:00.03]林俊杰-《曹操》 [00:13.35]作词:林秋离 [00:20.12]作曲:林俊杰 [00:25.32] [01:33.46][00:26.82]不是英雄 不读三国 [01:40.12][00:33.43]若是英雄 怎么能不懂寂寞 [02:39.68][01:46.34][00:39.63]独自走下长坂坡 月光太温柔 [02:43.20][01:49.82][00:43.15]曹操不啰嗦 一心要那荆州 [02:46.75][01:53.48][00:46.83]用阴谋 阳谋 明说 暗夺的摸 [02:53.44][02:00.10][00:53.50]东汉末年分三国 [02:56.37][02:03.15][00:56.52]烽火连天不休 [03:00.12][02:06.75][01:00.17]儿女情长 被乱世左右 [03:05.04][02:11.71][01:05.12]谁来煮酒 [03:06.78][02:13.45][01:06.84]尔虞我诈是三国 [03:09.85][02:16.43][01:09.73]说不清对与错 [03:13.38][02:20.11][01:13.48]纷纷扰扰 千百年以後 [03:18.44][02:25.06][01:18.45]一切又从头 [03:25.99][02:30.17][01:26.81]喔…… [88:88:88] </textarea> <div id="jquery_jplayer_1" class="jp-jplayer"></div> <div id="jp_container_1" class="jp-audio"> <div class="jp-type-single"> <div class="jp-gui jp-interface"> <ul class="jp-controls"> <li><a href="javascript:;" class="jp-play" tabindex="1">play</a></li> <li><a href="javascript:;" class="jp-pause" tabindex="1">pause</a></li> <li><a href="javascript:;" class="jp-stop" tabindex="1">stop</a></li> <li><a href="javascript:;" class="jp-mute" tabindex="1" title="mute">mute</a></li> <li><a href="javascript:;" class="jp-unmute" tabindex="1" title="unmute">unmute</a></li> <li><a href="javascript:;" class="jp-volume-max" tabindex="1" title="max volume">max volume</a></li> </ul> <div class="jp-progress"> <div class="jp-seek-bar"> <div class="jp-play-bar"></div> </div> </div> <div class="jp-volume-bar"> <div class="jp-volume-bar-value"></div> </div> <div class="jp-current-time"></div> <div class="jp-duration"></div> <ul class="jp-toggles"> <li><a href="javascript:;" class="jp-repeat" tabindex="1" title="repeat">repeat</a></li> <li><a href="javascript:;" class="jp-repeat-off" tabindex="1" title="repeat off">repeat off</a></li> </ul> </div> <div class="jp-details"> <ul> <li><span class="jp-title"></span></li> </ul> </div> <div class="jp-no-solution"> <span>Update Required</span> To play the media you will need to either update your browser to a recent version or update your <a href="http://get.adobe.com/flashplayer/" target="_blank">Flash plugin</a>. </div> </div> </div> <div class="content"> <ul id="lrc_list"> 加载歌词…… </ul> </div> </body> </html> 6、最后还是要提供下载地址: 微云下载歌词同步插件 (密码:LSm1) csdn 下载:http://download.csdn.net/detail/wk313753744/7803013
我在以前的一个老项目中使用的spring security的版本是spring-core-3.0.1.RELEASE.jar,其他的相关依赖的包也是这个版本的,但在我在新的项目中把包的版本升级成了3.24的;使用的配置文件还是用原来3.01的applicationContext-security.xml-old。 <spring.version>3.2.4.RELEASE</spring.version> <spring.security.version>3.2.4.RELEASE</spring.security.version> applicationContext-security.xml --old 版本是3.0.1 <?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd"> 上面我只写出了schema的约束,但是问题出现了,在新的项目部署到服务器时报如下错误了, 严重: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: You cannot use a spring-security-2.0.xsd or spring-security-3.0.xsd or spring-security-3.1.xsd schema with Spring Security 3.2. Please update your schema declarations to the 3.2 schema. Offending resource: URL [file:/D:/apache-tomcat-6.0.32/webapps/Fileserver/WEB-INF/classes/applicationContext-security.xml] at org.springframework.beans.factory.parsing.FailFastProblemReporter.fatal(FailFastProblemReporter.java:59) at org.springframework.beans.factory.parsing.ReaderContext.fatal(ReaderContext.java:68) at org.springframework.beans.factory.parsing.ReaderContext.fatal(ReaderContext.java:55) at org.springframework.security.config.SecurityNamespaceHandler.parse(SecurityNamespaceHandler.java:84) at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1438) at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1428) at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:185) at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:139) at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:108) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.registerBeanDefinitions(XmlBeanDefinitionReader.java:493) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:390) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:334) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:302) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:174) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:209) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:180) at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:125) at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:94) at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:130) at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:537) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:451) at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:389) at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:294) at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:112) at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4205) at org.apache.catalina.core.StandardContext.start(StandardContext.java:4704) at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:799) at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:779) at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:601) at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:1079) at org.apache.catalina.startup.HostConfig.deployDirectories(HostConfig.java:1002) at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:506) at org.apache.catalina.startup.HostConfig.start(HostConfig.java:1315) at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:324) at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:142) at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1061) at org.apache.catalina.core.StandardHost.start(StandardHost.java:840) at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1053) at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:463) at org.apache.catalina.core.StandardService.start(StandardService.java:525) at org.apache.catalina.core.StandardServer.start(StandardServer.java:754) at org.apache.catalina.startup.Catalina.start(Catalina.java:595) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:289) at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:414)上面中错误提到一句很关键的 You cannot use a spring-security-2.0.xsd or spring-security-3.0.xsd or spring-security-3.1.xsd schema with Spring Security 3.2. Please update your schema declarations to the 3.2 schema 也就是说我需要把我的schema升级成 3.2 以上的版本, 然后我在官方网站找到一个最新的一个配置文件:修改成如下就ok了 官方的配置实例 applicationContext-security.xml --new 版本是3.2.4 : <beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> <http> <intercept-url pattern="/**" access="ROLE_USER" /> <form-login /> <logout /> </http>这样就解决了。 然后额外说一下:在上面错误的同时也会报 java.lang.IllegalStateException: BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext。 其实大部分情况就是配置文件不多造成的, 等会 我把完整的项目贴到这儿:
1 这是北京交通大学的镜像站:http://mirror.bjtu.edu.cn/cn/ 2 这个站点有一个好处就是他不仅是操作系统的镜像站而且还要一写其他常用软件的仓库, 如Apache的常用软件 hbase 和hive等,http://mirror.bjtu.edu.cn/apache/,我学习hadoop就常用软件就在这里下载的, Hadopp的软件:http://mirror.bjtu.edu.cn/apache/hadoop/ Hbase的软件包:http://mirror.bjtu.edu.cn/apache/hbase/ Hive的软件包:http://mirror.bjtu.edu.cn/apache/hive/ zookeeper协调工具:http://mirror.bjtu.edu.cn/apache/zookeeper/ Apache服务器:http://mirror.bjtu.edu.cn/apache/httpd/ struts的开发包:http://mirror.bjtu.edu.cn/apache/struts/ tomcat6、7、8:http://mirror.bjtu.edu.cn/apache/tomcat/ 3 freebsd操作系统镜像,想想当年还学习过这个最安全的系统,但软件支持不好,port安装也不习惯,后来全用centos了.centos是gnu/linux的发行版,斯托曼强调说一定要说出gnu/linux才可以。http://mirror.bjtu.edu.cn/freebsd/ 4 我自己用的做多的服务器就是centos的也把地址发过来了:http://mirror.bjtu.edu.cn/centos/ 5 其他的如果你能看到这个博客 自己去下载好了
我测试是在windows7上测测试的, 需要准备的软件列表如: a. Apache2.2b. apache-tomcat-6.0.32-1我要集群的第一台tomcat服务器 c. apache-tomcat-6.0.32-2我要集群的第二台tomcat服务器 d. mod_jk.so 用户连接apache服务器和tomcat之间的组件 根据我个人的习惯,我喜欢先配置apache的http.conf之后,然后在去配置tomcat. 找到你的apache的安装目录如我的是在D:\Apache2.2,然后点击conf目录,你将会找到一个叫httpd.conf的配置文件,为了能让初学者能够明白,我只http.conf中的最后一行代码,代码最后几行如下:大概在490行左右添加 # Various default settings #Include conf/extra/httpd-default.conf #引入conf下mod_jk.conf include conf/mod_jk.conf #红色部分就是你在原来http.conf中需要加入的内容 # Secure (SSL/TLS) connections #Include conf/extra/httpd-ssl.conf # # Note: The following must must be present to support # starting without SSL on platforms with no /dev/random equivalent # but a statically compiled-in mod_ssl. # <IfModule ssl_module> SSLRandomSeed startup builtin SSLRandomSeed connect builtin </IfModule> 第二步就是在你安装的apache中的conf目录下加入两个文本文件mod_jk.conf和workers.properties.结构如下: 在文件mod_jk.conf的内容如下:该文本内容来源于网络.不知出处,若有原作发现 忘见谅 #D:\Apache2.2\conf\mod_jk.conf文件 #加载mod_jk Module LoadModule jk_module modules/mod_jk.so #指定workers.properties文件路径 JkWorkersFile conf/workers.properties #指定那些请求交给tomcat处理,"controller"为在workers.propertise里指定的负载分配控制器名 JkMount /* controller 然后再workers.properties的内容如下: #这是跟tomcat配置的重要文件D:\Apache2.2\conf\workers.properties #server worker.list = controller #========tomcat1======== #ajp13端口号,在tomcat下server.xml配置,默认8009 worker.tomcat1.port=8009 #tomcat的主机地址,如不为本机,请填写ip地址 worker.tomcat1.host=localhost #协议类型 worker.tomcat1.type=ajp13 #server的加权比重,值越高,分得的请求越多。lbfactor是负载平衡因数(Load Balance Factor) worker.tomcat1.lbfactor=1 #========tomcat2======== worker.tomcat2.port=8089 worker.tomcat2.host=localhost worker.tomcat2.type=ajp13 worker.tomcat2.lbfactor=1 #========controller,负载均衡控制器======== #负载均衡控制器类型,lbfactor是负载平衡因数(Load Balance Factor) worker.controller.type=lb #指定分担请求的tomcat列表 worker.controller.balanced_workers=tomcat1,tomcat2 #粘性session(默认是打开的) 当该属性值=true(或1)时,代表session是粘性的,即同一session在集群中的同一个节点上处理,session不跨越节点。在集群环境中,一般将该值设置为false worker.controller.sticky_session=false #设置用于负载均衡的server的session可否共享 worker.controller.sticky_session_force=1 然后重新启动apache服务器 能启动表示没问题,一般都不会有问题,到此apache中配置全部完毕。 记下来就是配置tomcat的,这里另外说一下,apache和tomcat集群其实实现很简单,原来就是通过ajp来实现的,只不过session复制的实现我还得在研究一下。不过我们用mod_jk.so来实现,session复制是已经实现了的、 tomcat配置这里要说明的是因为我是在同一台电脑,所以端口号一样是不能同时启动两个tomcat服务器的。所以首先要做的事就是修改tomcat1的server.xml配置文件后,复制到tomcat2下去 ,然后在修改端口号,这是你第一次配置会好点,不过不管你怎么弄 只要配置文件没错就行。我两台tomcat是这样子的: 然后修改后的C:\apache-tomcat-6.0.32-1\conf\server.xml文件内容如下:tomcat1啊 看仔细了 <?xml version='1.0' encoding='utf-8'?> <!--修改01 这是关闭tomcat的端口号在 第一台服务器的端口号都是默认的 第二台需要修改--> <Server port="8005" shutdown="SHUTDOWN"> <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" /> <Listener className="org.apache.catalina.core.JasperListener" /> <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" /> <Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" /> <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" /> <GlobalNamingResources> <Resource name="UserDatabase" auth="Container" type="org.apache.catalina.UserDatabase" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" pathname="conf/tomcat-users.xml" /> </GlobalNamingResources> <Service name="Catalina"> <!--修改02 这是tomcat的http监听端口号在 第一台服务器的端口号都是默认的 第二台也需要修改--> <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" URIEncoding="utf-8"/> <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> <!--修改03 jvmRoute="tomcat1" 这里的tomcat1一定要与的在D:\Apache2.2\conf\workers.properties目录下的名称要一直--> <Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat1"> <!--修改04 这是集群需要的 Cluster复制过去就行了 这里就是这样 不用特别修改--> <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="8"> <Manager className="org.apache.catalina.ha.session.DeltaManager" expireSessionsOnShutdown="false" notifyListenersOnReplication="true"/> <Channel className="org.apache.catalina.tribes.group.GroupChannel"> <Membership className="org.apache.catalina.tribes.membership.McastService" address="228.0.0.4" port="45564" frequency="500" dropTime="3000"/> <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver" address="auto" port="4000" autoBind="100" selectorTimeout="5000" maxThreads="6"/> <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter"> <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/> </Sender> <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/> <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/> </Channel> <Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter=""/> <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/> <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer" tempDir="/tmp/war-temp/" deployDir="/tmp/war-deploy/" watchDir="/tmp/war-listen/" watchEnabled="false"/> <ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/> <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/> </Cluster> <!--Cluster //END --> <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/> <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true" xmlValidation="false" xmlNamespaceAware="false"> </Host> </Engine> </Service> </Server> 然后tomcat2的我就是把配置文件粘贴过来了,就在tomcat1配置文件的基础上修改三处配置: 修改01 <Server port="8015" shutdown="SHUTDOWN"> 修改02 http端口号 <Connector port="8090" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" URIEncoding="utf-8"/> 修改03 这个端口要与你 这里的ajp端口号一定要与的在D:\Apache2.2\conf\workers.properties目录下的port要一直 <Connector port="8089" protocol="AJP/1.3" redirectPort="8443" /> C:\apache-tomcat-6.0.32-2\conf\server.xml配置信息如下: <?xml version='1.0' encoding='utf-8'?> <Server port="8015" shutdown="SHUTDOWN"> <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" /> <Listener className="org.apache.catalina.core.JasperListener" /> <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" /> <Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" /> <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" /> <GlobalNamingResources> <Resource name="UserDatabase" auth="Container" type="org.apache.catalina.UserDatabase" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" pathname="conf/tomcat-users.xml" /> </GlobalNamingResources> <Service name="Catalina"> <Connector port="8090" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" URIEncoding="utf-8"/> <Connector port="8089" protocol="AJP/1.3" redirectPort="8443" /> <Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat2"> <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="8"> <Manager className="org.apache.catalina.ha.session.DeltaManager" expireSessionsOnShutdown="false" notifyListenersOnReplication="true"/> <Channel className="org.apache.catalina.tribes.group.GroupChannel"> <Membership className="org.apache.catalina.tribes.membership.McastService" address="228.0.0.4" port="45564" frequency="500" dropTime="3000"/> <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver" address="auto" port="4000" autoBind="100" selectorTimeout="5000" maxThreads="6"/> <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter"> <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/> </Sender> <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/> <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/> </Channel> <Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter=""/> <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/> <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer" tempDir="/tmp/war-temp/" deployDir="/tmp/war-deploy/" watchDir="/tmp/war-listen/" watchEnabled="false"/> <ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/> <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/> </Cluster> <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/> <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true" xmlValidation="false" xmlNamespaceAware="false"> </Host> </Engine> </Service> </Server> 然后分别启动tomcat1和tomcat2看能不能正常启动, 我的能够正常启动了,然后接下来就是测试tomcat直接的session共享问题了. 我这儿有一个test.war包是提供session共享的,测试结果如下 tomcat1中 最后结果证明,成功集群,但是大型系统中如果才用session共享复制,可不是一个好办法,更好是通过内存共享,或者ip绑定技术,就是你第一次访问的服务器一直与你保持会话。 今天百度网盘又上不去了,过几天把黑马程序员-深度揭秘服务器端内幕公开课资料视频连接也写到这里 还有里面的配置文件都弄到这里好了 http://pan.baidu.com/s/1pJiCgFT
1.首先在你写的Action中添加这样一个方法,比如我的是MenuAction中添加的如下: // freemarker静态方法调用 public TemplateHashModel getStatics() { return BeansWrapper.getDefaultInstance().getStaticModels(); } 2.然后再你要使用的地方,我的是在menu_system_info.ftl中。如下: <tr> <td width="110"> Java版本: </td> <td> ${statics["java.lang.System"].getProperty("java.version")} </td> </tr> <tr> <td> 操作系统名称: </td> <td> ${statics["java.lang.System"].getProperty("os.name")} </td> </tr> 3. 常用信息获取如下: Java版本: ${statics["java.lang.System"].getProperty("java.version")} 显示结果为: 1.6.0_13 操作系统名称:${statics["java.lang.System"].getProperty("os.name")} 显示结果为: Windows Vista 操作系统构架:${statics["java.lang.System"].getProperty("os.arch")} 显示结果为: x86 操作系统版本:${statics["java.lang.System"].getProperty("os.version")} 显示结果为: 6.1 当前工作目录:${statics["java.lang.System"].getProperty("user.dir")} 显示结果为: D:\apache-tomcat-6.0.32\bin 临时文件路径:${statics["java.lang.System"].getProperty("java.io.tmpdir")} 显示结果为: D:\apache-tomcat-6.0.32\temp 4.java系统信息参数, java.vendor Java 运行时环境供应商 java.vendor.url Java 供应商的 URL java.home Java 安装目录 java.vm.specification.version Java 虚拟机规范版本 java.vm.specification.vendor Java 虚拟机规范供应商 java.vm.specification.name Java 虚拟机规范名称 java.vm.version Java 虚拟机实现版本 java.vm.vendor Java 虚拟机实现供应商 java.vm.name Java 虚拟机实现名称 java.specification.version Java 运行时环境规范版本 java.specification.vendor Java 运行时环境规范供应商 java.specification.name Java 运行时环境规范名称 java.class.version Java 类格式版本号 java.class.path Java 类路径 java.library.path 加载库时搜索的路径列表 java.io.tmpdir 默认的临时文件路径 java.compiler 要使用的 JIT 编译器的名称 java.ext.dirs 一个或多个扩展目录的路径 os.name 操作系统的名称 os.arch 操作系统的架构 os.version 操作系统的版本 file.separator 文件分隔符(在 UNIX 系统中是“/”) path.separator 路径分隔符(在 UNIX 系统中是“:”) line.separator 行分隔符(在 UNIX 系统中是“/n”) user.name 用户的账户名称 user.home 用户的主目录 user.dir 用户的当前工作目录
这是一个采用maven项目管理工具,集成struts2+spring3+hibernate3+mysql的一套框架模板,只是提供集成成功,struts2我采用的是零配置方式的,如果对着stuts2零配置方式不理解的,则回头学习一下。该程序测试时在jboss7 AS上运行成功。但是没有使用jndi数据源,在jboss7中数据源配置需要按照module的方式配置,但是jboss4中数据源则相对简单一点。jboss4中使用spring和struts2集成时会有问题,这里有一个解决的文章可以参考一下:http://blog.csdn.net/zgmzyr/article/details/6893537.我的该框架在tomcat7下也是测试通过的,提示:如果阁下的maven仓库没有这些pom.xml,可以去下载后放到项目的lib目录下也是可以的。 1、这是我maven项目的pom.xml文件 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.veight</groupId> <artifactId>veight</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <name>veight</name> <description /> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <build> <sourceDirectory>src</sourceDirectory> <resources> <resource> <directory>src</directory> <excludes> <exclude>**/*.java</exclude> </excludes> </resource> </resources> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>2.3.2</version> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin> <plugin> <artifactId>maven-war-plugin</artifactId> <version>2.2</version> <configuration> <warSourceDirectory>${basedir}/WebRoot</warSourceDirectory> <version>3.0</version> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> </plugins> </build> <dependencies> <!-- spring 3.2 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>3.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>3.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>3.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>3.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>3.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>3.2.0.RELEASE</version> </dependency> <!-- Struts 2 --> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-core</artifactId> <version>2.3.15.2</version> </dependency> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-json-plugin</artifactId> <version>2.3.15.2</version> </dependency> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-spring-plugin</artifactId> <version>2.3.15.2</version> </dependency> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-convention-plugin</artifactId> <version>2.3.15</version> </dependency> <!-- hibernate 3.6 --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>3.6.10.Final</version> </dependency> <!-- JPA --> <dependency> <groupId>org.hibernate.javax.persistence</groupId> <artifactId>hibernate-jpa-2.0-api</artifactId> <version>1.0.1.Final</version> </dependency> <!-- log4j --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.2</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.16</version> </dependency> <!-- C3P0 --> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> <!-- mysql5.1.30 驱动包 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.30</version> </dependency> </dependencies> </project> 2、struts2的配置文件struts.xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <!-- 在struts2.3.15 admin!login。action,找不到action的错误 开启动态调用方法 --> <constant name="struts.enable.DynamicMethodInvocation" value="true"/> <!-- 是否显示详细错误信息 --> <constant name="struts.devMode" value="false" /> <!-- 配置使用Spring管理Action --> <constant name="struts.objectFactory" value="spring" /> <!-- 后缀 --> <constant name="struts.action.extension" value="action" /> <!-- 国际化资源文件名称 --> <constant name="struts.custom.i18n.resources" value="i18n" /> <!-- 是否自动加载国际化资源文件 --> <constant name="struts.i18n.reload" value="false" /> <!-- convention类重新加载 --> <constant name="struts.convention.classes.reload" value="false" /> <!-- 浏览器是否缓存静态内容 --> <constant name="struts.serve.static.browserCache" value="true" /> <!-- 上传文件大小限制设置 --> <constant name="struts.multipart.maxSize" value="-1" /> <!-- 主题 --> <constant name="struts.ui.theme" value="simple" /> <!-- 编码 --> <constant name="struts.i18n.encoding" value="UTF-8" /> <!-- 结果资源的路径 --> <constant name="struts.convention.result.path" value="/WEB-INF/template/" /> <!-- URL资源分隔符 --> <constant name="struts.convention.action.name.separator" value="_" /> <package name="basePackage" extends="struts-default"> <interceptors> <interceptor-stack name="baseStack"> <interceptor-ref name="exception" /> <interceptor-ref name="alias" /> <interceptor-ref name="servletConfig" /> <interceptor-ref name="i18n" /> <interceptor-ref name="prepare" /> <interceptor-ref name="chain" /> <interceptor-ref name="debugging" /> <interceptor-ref name="scopedModelDriven" /> <interceptor-ref name="modelDriven" /> <interceptor-ref name="fileUpload" /> <interceptor-ref name="checkbox" /> <interceptor-ref name="multiselect" /> <interceptor-ref name="staticParams" /> <interceptor-ref name="actionMappingParams" /> <interceptor-ref name="params"> <param name="excludeParams">dojo\..*,^struts\..*</param> </interceptor-ref> <interceptor-ref name="conversionError"/> <!-- 配置方法级别的校验 --> <interceptor-ref name="validation"> <param name="excludeMethods">input,back,cancel,browse</param> <param name="validateAnnotatedMethodOnly">true</param> </interceptor-ref> <interceptor-ref name="workflow"> <param name="excludeMethods">input,back,cancel,browse</param> </interceptor-ref> </interceptor-stack> </interceptors> <!-- 配置默认拦截器栈 --> <default-interceptor-ref name="baseStack" /> <!-- 未到找Action指向页面 --> <default-action-ref name="errorPage" /> <action name="errorPage"> <result type="redirect">/html/error_page_404.html</result> </action> </package> <!-- 后台管理 Action --> <package name="admin" extends="basePackage" namespace="/admin/"> <interceptors> <interceptor-stack name="adminStack"> <interceptor-ref name="baseStack" /> </interceptor-stack> </interceptors> <global-results> <result name="error" type="freemarker">/WEB-INF/template/admin/error.ftl</result> <result name="success" type="freemarker">/WEB-INF/template/admin/success.ftl</result> </global-results> </package> </struts>3、重要spring配置文件applicationContext.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!-- 使用注解注入 --> <context:annotation-config /> <!-- JDBC参数配置 --> <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" lazy-init="true"> <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" /> <property name="ignoreResourceNotFound" value="true" /> <property name="locations"> <list> <value>classpath:/jdbc.properties</value> </list> </property> </bean> <!-- 连接池配置 C3p0--> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driver}"></property> <property name="jdbcUrl" value="${jdbc.url}"></property> <property name="user" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> </bean> <!-- 配置hibernate信息 --> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> <!-- 配置Hibernate拦截器,自动填充数据的插入、更新时间 --> <property name="dataSource" ref="dataSource" /> <property name="hibernateProperties"> <value> <!-- 设置数据库方言 --> hibernate.dialect=${hibernate.dialect} <!-- 设置自动创建|更新|验证数据库表结构 --> hibernate.hbm2ddl.auto=update <!-- 输出SQL语句到控制台 --> hibernate.show_sql=${hibernate.show_sql} <!-- 格式化输出到控制台的SQL语句 --> hibernate.format_sql=${hibernate.format_sql} <!-- 是否开启二级缓存 --> hibernate.cache.use_second_level_cache=false <!-- 配置二级缓存产品 --> hibernate.cache.provider_class=org.hibernate.cache.OSCacheProvider <!-- 是否开启查询缓存 --> hibernate.cache.use_query_cache=false <!-- 数据库批量查询数 --> hibernate.jdbc.fetch_size=50 <!-- 数据库批量更新数 --> hibernate.jdbc.batch_size=30 </value> </property> <!-- 注入实体对象 --> <property name="annotatedClasses"> <list> <value>org.veight.admin.domain.Admin</value> </list> </property> </bean> <!-- 设置需要进行Spring注解扫描的类包 --> <context:component-scan base-package="org.veight" /> <!-- 配置freemarkerManager --> <bean id="freemarkerManager" class="org.apache.struts2.views.freemarker.FreemarkerManager" /> </beans> 4、JDBC的属性文件在spring中要引用,你也可以根据自己的情况直接写在spring的配置文件中,jdbc.properties #SHOP++ JDBC\u914D\u7F6E\u6587\u4EF6 #Wed Mar 13 22:12:01 CST 2013 jdbc.url=jdbc\:mysql\://localhost\:3306/test?useUnicode\=true&characterEncoding\=UTF-8 jdbc.username=root hibernate.dialect=org.hibernate.dialect.MySQLDialect jdbc.driver=com.mysql.jdbc.Driver hibernate.show_sql=false jdbc.password=123456 hibernate.format_sql=false log4j.properties: log4j.rootCategory=error,stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=[SHOP++] %p [%t] %C.%M(%L) | %m%n log4j.logger.com.opensymphony.xwork2.ognl.OgnlValueStack=ERROR 5、在struts2中我喜欢采用零配置,为了把freemarker也整合进来freemarker.properties classic_compatible=true whitespace_stripping=true template_update_delay=3600 locale=zh_CN default_encoding=utf-8 url_escaping_charset=utf-8 date_format=yyyy-MM-dd time_format=HH:mm:Ss datetime_format=yyyy-MM-dd number_format=\#0.\#\#\#\#\# 6、工程项目就是veight,顺便说一句我的开发工具是myeclipse2013,所以直接导入应该ibu兼容的,最好是自己创建一个工程,然后复制配置文件到你工程中去。源代码我放在我自己资源中,如果在tomcat7或者在jboss7 as中部署,启动服务器,在Mysql数据库的test库中会生成一张admin的表,则说明成功。 7 、前面忘记家里web.xml的配置文件了web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>veight</display-name> <!-- spring配置文件位置 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!-- spring核心监听器 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- struts核心控制器 --> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>*.action</url-pattern> </filter-mapping> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> </web-app> 6、源码下载地址,不要积分:源码工程下载地址
<pre code_snippet_id="337288" snippet_file_name="blog_20140510_1_5361656" name="code" class="html"><!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Clean jPlayer skin: Example</title> <link rel="stylesheet" href="/projects/clean-jplayer-skin/player.css"> <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script> <script type="text/javascript" src="/projects/clean-jplayer-skin/jquery.jplayer.js"></script> <script type="text/javascript" src="/projects/clean-jplayer-skin/jplayer.cleanskin.js"></script> <script type="text/javascript"> $(document).ready(function() { //视频播放的地址 var media = { m4v: '/projects/clean-jplayer-skin/Tomorrowland_2013_official_aftermovie.mp4', poster: '/projects/clean-jplayer-skin/Tomorrowland_2013_official_aftermovie.mp4' } //jplayer的参数列表 var options = { //初始化播放器 ready: function () { $(this).jPlayer("setMedia", media); }, //自定义样式开关,不开启也不影响 //customCssIds:true; // Extra Settings //注意的swf的路径,不能写错的,如果是根目录写一个点 swfPath: "/img/jplayer.swf", supplied: 'm4v', solution: 'html, flash', //音量 总共是1,0.5代表50%; volume: 0.5, size: size, smoothPlayBar: false, keyEnabled: true, // CSS Selectors //播放器器的包裹div的class样式 cssSelectorAncestor: '.playerGUI', //这是jplayer默认使用的样式,如果根据自己的实际情况修改, //我记得以前要使用自定义的样式要开启 customCssIds: true, cssSelector: { videoPlay: ".video-play", play: ".play", pause: ".pause", seekBar: ".seekBar", playBar: ".playBar", volumeBar: ".currentVolume", volumeBarValue: ".currentVolume .curvol", currentTime: ".time.current", duration: ".time.duration", fullScreen: ".fullScreen", restoreScreen: ".fullScreenOFF", gui: ".controls", noSolution: ".noSolution" }, //播放器出错 回调函数 error: function(event) { if(ready && event.jPlayer.error.type === $.jPlayer.error.URL_NOT_SET) { // Setup the media stream again and play it. $(this).jPlayer("setMedia", media).jPlayer('play'); } //播放器点击播放按钮 play: function() { $('#player1 .video-play').fadeOut(); $(this).on('click', function() { $('#player1').jPlayer('pause');}); $(this).jPlayer("pauseOthers"); }, //暂回调函数 pause: function() { $('#player1 .video-play').fadeIn(); $('#player1 .playerScreen').unbind('click'); }, //声音改变回调函数 volumechange: function(event) { if(event.jPlayer.options.muted) { $('#player1 .currentVolume').val(0); } else { $('#player1 .currentVolume').val(event.jPlayer.options.volume); } }, //这个名字不知道有什么用,应该叫进度条好些吧 timeupdate: function(event) { $('#player1 .seekBar').val(event.jPlayer.status.currentPercentRelative); }, // 播放完毕后的回调函数 ended: function() { $(this).jPlayer("setMedia", media); } }; // 创建声音控制条 $('#player1 .currentVolume').slider({ range: [0, 1], step: 0.01, start : 0.5, handles: 1, slide: function() { var value = $(this).val(); $(mainPlayer).jPlayer("option", "muted", false); $(mainPlayer).jPlayer("option", "volume", value); $('#player1 .volumeText').html('Volume: ' + (value * 100).toFixed(0) + ''); } }); $('#player1 .seekBar').slider({ range: [0,100], step: 0.01, start: 0, handles: 1, slide: function() { var value = $(this).val(); $('#player1').jPlayer("playHead", value); } }); // Initialize Player $('#player1').jPlayer(options); }); </script> </head> <body> <div id="player1" class="playerGUI"> <div id="videoPlayer"></div> <div class="playerScreen"> <a tabindex="1" href="#" class="video-play"></a> </div> <div class="controls"> <div class="leftblock"> <a tabindex="1" href="#" class="play smooth"></a> <a tabindex="1" href="#" class="pause smooth"></a> </div> <div class="progress"> <span>Tomorrowland 2013 - Aftermovie</span> <div class="progressbar"> <div class="seekBar"> <div class="playBar"></div> </div> </div> <div class="time current">00:00</div> <div class="time duration">00:00</div> </div> <div class="rightblock"> <div class="volumeBar"> <div class="currentVolume"><div class="curvol"></div></div> </div> <a class="volumeText">Volume: 50</a> <a href="#" tabindex="1" class="fullScreen smooth"></a> <a href="#" tabindex="1" class="fullScreenOFF smooth"></a> </div> </div> </div> </body> </html>
友情提示:后面在2014 7 29 日有增加一个新的解决方法和问题原因的说明 笔者在开发一个java ssh+mysql的项目过程中,本来在windows下开发测试,在myeclipse中的部署都没有问题,但当想要上线测试到linux服务器时,我预先做了移植测试,在Linux上,在启动过程中,如果验证码图片是采用背景图片的,在windows下没问题,但是在linux(centos5.5)上就会有问题:异常信息如下: 1. 导致的原因 Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'net.shopxx.common.JCaptchaEngine#ee20ab' defined in file [/usr/local/apache-tomcat-6.0.32/webapps/shopx/WEB-INF/classes/applicationContext.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [net.shopxx.common.JCaptchaEngine]: Constructor threw exception; nested exception is com.octo.captcha.CaptchaException: Unknown error during file reading Caused by: javax.imageio.IIOException: Not a JPEG file: starts with 0x42 0x4d at com.sun.imageio.plugins.jpeg.JPEGImageReader.readImageHeader(Native Method) at com.sun.imageio.plugins.jpeg.JPEGImageReader.readNativeHeader(JPEGImageReader.java:603) at com.sun.imageio.plugins.jpeg.JPEGImageReader.checkTablesOnly(JPEGImageReader.java:341) at com.sun.imageio.plugins.jpeg.JPEGImageReader.gotoImage(JPEGImageReader.java:475) at com.sun.imageio.plugins.jpeg.JPEGImageReader.readHeader(JPEGImageReader.java:596) at com.sun.imageio.plugins.jpeg.JPEGImageReader.readInternal(JPEGImageReader.java:1042) at com.sun.imageio.plugins.jpeg.JPEGImageReader.read(JPEGImageReader.java:1022) at sun.awt.image.codec.JPEGImageDecoderImpl.decodeAsBufferedImage(JPEGImageDecoderImpl.java:89) at com.octo.captcha.component.image.backgroundgenerator.FileReaderRandomBackgroundGenerator.getImage(FileReaderRandomBackgroundGenerator.java:201) ... 75 more 2014-4-22 2:15:55 org.apache.catalina.core.StandardContext start Caused by: java.awt.HeadlessException: No X11 DISPLAY variable was set, but this program performed an operation which requires it. Caused by: com.octo.captcha.CaptchaException: Unknown error during file reading 2 问题分析 最关键部分就是这句错误,在读文件时发生未知错误,这个问题的因该是文件系统路径造成的, 我说一下如何解决的办法: 在原来的JCaptchaEngine这个类中,路径是这样写的,这个是测试shopxx,也有这个问题,我直接使用shopxx来说了,如果需要看shopxx的源码直接发送邮件到(845885222@qq.com) private static final String IMAGE_PATH = "./net/shopxx/captcha/";// 随机背景图片路径 你会在JCaptchaEngine类文件中找到如下代码,这句代码的意思是,在获取验证码时采用不同的背景图片,背景图片的位置放在IMAGE_PATH指定的目录下。 BackgroundGenerator backgroundGenerator = new FileReaderRandomBackgroundGenerator(IMAGE_WIDTH, IMAGE_HEIGHT, IMAGE_PATH); 3 解决办法 方法一:不使用原有的图片作为验证码的背景了,而是使用渐变色代替验证码的背景 在验证码获取时修改成不要验证码背景,也就是把验证码背景颜色设置为纯色, 替换上边一行,代码如下: //背景颜色随机生成 // BackgroundGenerator backgroundGenerator = new GradientBackgroundGenerator(IMAGE_WIDTH, IMAGE_HEIGHT, Color.white,Color.white); 方法二:把private static final String IMAGE_PATH = "./net/shopxx/captcha/";// 随机背景图片路径下的不是jpg格式的图片删除,shopxx中在图片验证码背景图片文件夹下多加了一个captcha_bg_61.bmp的图片,我把该图片删了,一切正常。 4. 重启服务器, 更新日期:2014-07-29 早9:24 5.更新该文档后的总结,在第一次解决这个问题的时候,我自认为是验证码背景图片文件夹路径在window上和linux不一致引起的,其实不然,报的错误有一个非常明显得提示如下: Caused by: javax.imageio.IIOException: Not a JPEG file: starts with 0x42 0x4d 上面红色的异常提示说不是一个jpeg文件,我在查找我背景图片存放的目录后,果然发现一个captcha_bg_61.bmp的图片文件,很显然,这个bmp的图片是window特有的文件,这只是可以解释的原因之一。 6.从JCaptcha源码解释说明: FileReaderRandomBackgroundGenerator.java private static BufferedImage getImage(File o) { BufferedImage out = null; try { // ImageInfo info = new ImageInfo(); // Image image = ToolkitFactory.getToolkit().createImage(o.toString()); // info.setInput(new FileInputStream(o)); // out = new BufferedImage(info.getWidth(), info.getHeight(),BufferedImage.TYPE_INT_RGB ); // out.getGraphics().drawImage(image,out.getWidth(),out.getHeight(),null); // out.getGraphics().dispose(); // FileInputStream fis = new FileInputStream(o); JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(fis); out = decoder.decodeAsBufferedImage(); fis.close(); // Return the format name return out; } catch (IOException e) { throw new CaptchaException("Unknown error during file reading ", e); } catch (ImageFormatException e) { return null; } } 从代码中可以看出decoder是JPEGCodec的,其实这个也是我知道问题所在了,然后才这样理解的,总之一句话,在你存放验证码背景图片的文件夹src ./net/shopxx/captcha/ 下,只要存放的都是真正jpg的文件,真正你懂的,就是不要通过改后缀名过来的。而是你存储的时候就是.jpg的,搞定收工。
Struts2注解 1 Struts2注解的作用 使用注解可以用来替换struts.xml配置文件!!! 2 导包 必须导入struts2-convention-plugin-2.3.15.jar包,它在struts2安装包下lib目录中。 3 通过配置文件学习对应的注解 @Action来代替<action>元素! l String value():指定访问路径; l Result[] results():指定局部结果。 @Result来代替<result>元素! l String name():指定结果名称; l String location():指定结果路径。 @Namespace代替<package>的namespace属性: l String value():指定名称空间。 @ParentPackage代替<package>的extends属性: l String value():指定父包名称 @Namespace("/order") @ParentPackage("struts-default") publicclass OrderAction extends ActionSupport { @Action(value="add", results={ @Result(name="success", location="/index.jsp"), @Result(name="input", location="/add.jsp")}) public String add() { System.out.println("add()"); returnSUCCESS; } @Action(value="mod", results={@Result(name="success", location="/index.jsp")}) public String mod() { System.out.println("mod()"); returnSUCCESS; } @Action(value="del", results={@Result(name="success", location="/index.jsp")}) public String del() { System.out.println("del()"); returnSUCCESS; } } 4 注解相关常量 struts.convention.classes.reload,默认值为false。表示在修改注解后需要重启Tomcat! Struts2约定(了解) 1 什么是约定 使用约定可以连注解都不写,是真正的零配置,但是它的能力有限,所以这种方式不可取的。 l Struts2会对Action的命名,以及Action的包名都有限制; l Struts2会对结果页面的存放路径,以及结果页面的名称也都有限制。 使用约定与使用注解一样,也要导包:struts2-convention-plugin-2.3.15.jar 1 约定对Action的限制 约定Action类名: 要求Action的命名必须以“Action”为后缀,例如:UserAction、BookAction等等。可以使用下面常量来修改后缀限制: <constant name="struts.convention.action.suffix" value="Action"/> 约定Action包名: 要求Action必须放到action、actions、struts、struts2包中。例如:cn.itcast.action、cn.itcast.sturts、cn.itcast.action.user等等。可以使用下面常量来修改Actoin的包 <constant name="struts.convention.package.locators" value="action,actions,struts,struts2"/> 3 通过Action的限制得到访问路径 上面已经知道约定对Action的限制,现在通过Action的包名和类名得到它的访问路径 例如:cn.itcast.action.UserAction,它的访问路径为/user.action 例如:cn.itcast.action.user.UserAction,它的访问路径为/user/user.action。因为在约定包action后面还有一层包user,那么这一层就是名称空间了。 例如:com.action.cn.itcast.user.UserAction,它的访问路径为/cn/itcast/user/user.action。 例如:cn.itcast.action.user.UserListAction, 它的访问路径为/user/user-list.action。当Action名称由多个单词构成,那么在访问路径中使用“-”来连接多个单词。 4 通过Action的限制得到结果页面路径 Struts2约定限制结果页面必须放到/WEB-INF/content/目录中,可以通过下面常量来修改这个限制。 <constant name="struts.convention.result.path" value="/WEB-INF/content/"/> 当cn.itcast.action.UserAction返回结果为success时,Struts会依次查找如下结果页面: l /WEB-INF/content/user-success.jsp; l /WEB-INF/content/user-success.html; l /WEB-INF/content/user.jsp 当cn.itcast.action.user.UserListAction返回的结果为error时,Struts会依次查找如下结果页面: l /WEB-INF/content/user/user-list-error.jsp; l /WEB-INF/content/user/user-list-error.html; l /WEB-INF/content/user/user-list.jsp。
MERCury 2D Game Engine 下载地址https://github.com/weslgames/MERCury 维护增强和可靠的编码引擎的wessles www.wessles.com Libgdx, and slick2d. Those are both GREAT libraries, way more than this will ever be. 都,和slick2d。这些都是很好的图书馆,比这将永远。 However... 然而… That is why I made this. Sure, they are great, but they are LARGE. They have tons of utilities that few people use, like Slick2d's TileMap class. MERCury is compact, and gets the job done still at a high framerate. 这就是为什么我做了这个。当然,他们都是伟大的,但他们都大。他们有大量的公用事业,很少有人用,像slick2d的tilemap类。水星是紧凑的,和取得的工作,仍处于一个高的帧率。 You can stick to libGDX, Slick2d, but if you want a small lib/engine, you have to look around (as you could guess, smaller engines/libs are less popular). 你能坚持都,slick2d,但如果你想要一个小库/引擎,你必须看看(你能猜出,更小的发动机/ LIBS是不受欢迎的)。 But I am not pitching this to you, I am sharing my progress on this project. This is for my learning benifit. If it comes in handy for you, then I feel like I have accomplished that much more! 但我不投给你,我对这个项目共享我的进步。这是我的学习效益。如果它是方便你,然后我觉得我已经完成了多少!
原文地址:https://www.web-tinker.com/article/20306.html 知道了怎么握手只是让客户端和服务器建立连接而已,WebSocket真正麻烦的地方是在数据的传输上!为了环保,它使用了特定格式的数据帧,这个数据帧需要自己去解析(当然也有别人编写好的库可以用)。虽然官方文档描述的很详细,但是看起来还是蛋疼。 当客户端向服务器发送一个数据时服务器收到一个数据帧,比如下面的程序: //客户端程序 var ws=new WebSocket("ws://127.0.0.1:8000"); ws.onopen=function(e){ ws.send("次碳酸钴"); //发送数据 };=============================================================== //服务器程序 var crypto=require('crypto'); var WS='258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; require('net').createServer(function(o){ var key; o.on('data',function(e){ if(!key){ //握手 key=e.toString().match(/Sec-WebSocket-Key: (.+)/)[1]; key=crypto.createHash('sha1').update(key+WS).digest('base64'); o.write('HTTP/1.1 101 Switching Protocols\r\n'); o.write('Upgrade: websocket\r\n'); o.write('Connection: Upgrade\r\n'); o.write('Sec-WebSocket-Accept: '+key+'\r\n'); o.write('\r\n'); }else onmessage(e); //接收并交给处理函数 }); }).listen(8000); 这里是直接把接收到的数据输出了,得到这样一个东西 这就是一个完整的数据帧,直接的16进制数据我们当然无法直接阅读,需要按照数据帧的格式把它里面的数据取出来才行。对于这个数据帧,官方文档提供了一个结构图 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-------+-+-------------+-------------------------------+ |F|R|R|R| opcode|M| Payload len | Extended payload length | |I|S|S|S| (4) |A| (7) | (16/64) | |N|V|V|V| |S| | (if payload len==126/127) | | |1|2|3| |K| | | +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + | Extended payload length continued, if payload len == 127 | + - - - - - - - - - - - - - - - +-------------------------------+ | |Masking-key, if MASK set to 1 | +-------------------------------+-------------------------------+ | Masking-key (continued) | Payload Data | +-------------------------------- - - - - - - - - - - - - - - - + : Payload Data continued ... : + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Payload Data continued ... | +---------------------------------------------------------------+ 光拿出这个实在很难看懂,顶部数字用十进制而不是八进制太让人蛋疼了。当然官方文档在后面的描述中也有详细介绍,看完后再回头来看图表才能看明白。其实WebSocket目前还不太完善,很多实验性的东西,所以完全按照官方文档来理解是蛋疼的。这里就说我自己的理解。 现在再看左上角上面的图标,左上角的四个小列,也就是4位,第一位是FIN,后面三位是RSV1到3。官方文档上说RSV是预留的空间,正常为0,这就意味着,正常情况下他们可以当做0填充,那么前4位只有第一位的FIN需要设置,FIN表示帧结束,由于这篇中它不重要就不特别介绍了。接着后面的四位是储存opcode的值,这个opcode是标识数据类型的。这样数据的第一个字节我们就能理解它的含义了,看上面16进制的数据的第一个字节81换成二进制是1000001,第一个1是FIN的值,最后一个1是opcode的值。 接着是第二个字节的数据,它由1位的MASK和7位的PayloadLen组成,MASK标识这个数据帧的数据是否使用掩码,PayloadLen表示数据部分的长度。但是PayloadLen只有7位,换成无符号整型的话只有0到127的取值,这么小的数值当然无法描述较大的数据,因此规定当数据长度小于或等于125时候它才作为数据长度的描述,如果这个值为126,则时候后面的两个字节来储存储存数据长度,如果为127则用后面八个字节来储存数据长度。所以上面的图片第一行的最右侧那块和第二行看起来有些颓然。从我们的示例数据来看,第二个字节的8C中80是最高位为1,这意味着MASK为1,后面的C表示这个数据部分有12个字节。 再接着是上面图表中的MaskingKey,它占四个字节,储存掩码的实体部分。但是只有在前面的MASK被设置为1时候才存在这个数据,否则不使用掩码也就没有这个数据了。看我们的示例数据,由于前面的MASK为1,所以3到6字节的“79 77 3d 41”是数据的掩码实体。 最后是数据部分,如果掩码存在,那么所有数据都需要与掩码做一次异或运算,四个字节的掩码与所有数据字节轮流发生性关系。如果不存在掩码,那么后面的数据就可以直接使用。 这样数据帧就解析完了。下面是我写的数据帧解析的程序,请不要吐槽代码没优化 function decodeDataFrame(e){ var i=0,j,s,frame={ //解析前两个字节的基本数据 FIN:e[i]>>7,Opcode:e[i++]&15,Mask:e[i]>>7, PayloadLength:e[i++]&0x7F }; //处理特殊长度126和127 if(frame.PayloadLength==126) frame.PayloadLength=(e[i++]<<8)+e[i++]; if(frame.PayloadLength==127) i+=4, //长度一般用四字节的整型,前四个字节通常为长整形留空的 frame.PayloadLength=(e[i++]<<24)+(e[i++]<<16)+(e[i++]<<8)+e[i++]; //判断是否使用掩码 if(frame.Mask){ //获取掩码实体 frame.MaskingKey=[e[i++],e[i++],e[i++],e[i++]]; //对数据和掩码做异或运算 for(j=0,s=[];j<frame.PayloadLength;j++) s.push(e[i+j]^frame.MaskingKey[j%4]); }else s=e.slice(i,i+frame.PayloadLength); //否则直接使用数据 //数组转换成缓冲区来使用 s=new Buffer(s); //如果有必要则把缓冲区转换成字符串来使用 if(frame.Opcode==1)s=s.toString(); //设置上数据部分 frame.PayloadData=s; //返回数据帧 return frame; }; 既然有了解析程序,那么我们就可以把上面实例服务器端的onmessage方法修改一下 function onmessage(e){ e=decodeDataFrame(e); //解析数据帧 console.log(e); //把数据帧输出到控制台 }; 这样服务器接收客户端穿过了的数据就没问题了。嘛,这篇文章就只说接收,至于从服务器发送到客户的情况会有更复杂的情况出现,咱下一篇再说。
1 关于hibernate的@Formula用法和作用可以参照别的文章 2 hibernate提取属性也叫做计算属性,该属性值是一个值读属性,是通过使用sql语句获取得到的,常用的是统计数据 3 案例:我有一个employee实体类,该类有主键Id或name,月薪水(monthlySalary)等属性(字段),你现在有一个想法想要获取该employee的年薪,计算方式是月薪*12个月=年薪。 package net.ozar.exp.entity; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name="EMPLOYEE") public class Employee implements java.io.Serializable { private static final long serialVersionUID = -7311873726885796936L; @Id @Column(name="ID") private Integer id; @Column(name="FIRST_NAME", length=31) private String firstName; @Column(name="LAST_NAME", length=31) private String lastName; @Column(name="MONTHLY_SALARY") private float monthlySalary; public Employee() { } // getters and setters // ... public float getMonthlySalary() { return monthlySalary; } public void setMonthlySalary(float monthlySalary) { this.monthlySalary = monthlySalary; } /* This artificial property - as I call it - is a kind of a calculated property, but not with Hibernate derived property support - not just yet */ public float getYearlySalary() { return this.monthlySalary * 12; } }4 上面这个是没有加入hibernate @@Formula支持的用法,现在我们加入这个注解实现: package net.ozar.exp.entity; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import org.hibernate.annotations.Formula; @Entity @Table(name="EMPLOYEE") public class Employee implements java.io.Serializable { private static final long serialVersionUID = -7311873726885796936L; @Id @Column(name="ID") private Integer id; @Column(name="FIRST_NAME", length=31) private String firstName; @Column(name="LAST_NAME", length=31) private String lastName; @Column(name="MONTHLY_SALARY") private float monthlySalary; @Formula("MONTHLY_SALARY*12") private float yearlySalary; public Employee() { } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public float getMonthlySalary() { return monthlySalary; } public void setMonthlySalary(float monthlySalary) { this.monthlySalary = monthlySalary; } public float getYearlySalary() { return yearlySalary; } }5 注意到上面这个案例的@Formla的值是关联到“MONTHLY_SALARY”而yearlySalry属性是没有存储如数据库的。 6 一些更为复杂用法 @Formula("(select min(l.creation_date) from Logs l where l.customer_id = id)") private Date firstLoginDate;注意:最後面的customer_id=id中后面的id的值也就当前对象的id值
我在以前的一个老项目中使用的spring security的版本是spring-core-3.0.1.RELEASE.jar,其他的相关依赖的包也是这个版本的,但在我在新的项目中把包的版本升级成了3.24的;使用的配置文件还是用原来3.01的applicationContext-security.xml-old。 <spring.version>3.2.4.RELEASE</spring.version> <spring.security.version>3.2.4.RELEASE</spring.security.version> applicationContext-security.xml --old 版本是3.0.1 <?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd"> 上面我只写出了schema的约束,但是问题出现了,在新的项目部署到服务器时报如下错误了, 严重: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: You cannot use a spring-security-2.0.xsd or spring-security-3.0.xsd or spring-security-3.1.xsd schema with Spring Security 3.2. Please update your schema declarations to the 3.2 schema. Offending resource: URL [file:/D:/apache-tomcat-6.0.32/webapps/Fileserver/WEB-INF/classes/applicationContext-security.xml] at org.springframework.beans.factory.parsing.FailFastProblemReporter.fatal(FailFastProblemReporter.java:59) at org.springframework.beans.factory.parsing.ReaderContext.fatal(ReaderContext.java:68) at org.springframework.beans.factory.parsing.ReaderContext.fatal(ReaderContext.java:55) at org.springframework.security.config.SecurityNamespaceHandler.parse(SecurityNamespaceHandler.java:84) at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1438) at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1428) at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:185) at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:139) at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:108) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.registerBeanDefinitions(XmlBeanDefinitionReader.java:493) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:390) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:334) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:302) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:174) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:209) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:180) at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:125) at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:94) at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:130) at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:537) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:451) at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:389) at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:294) at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:112) at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4205) at org.apache.catalina.core.StandardContext.start(StandardContext.java:4704) at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:799) at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:779) at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:601) at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:1079) at org.apache.catalina.startup.HostConfig.deployDirectories(HostConfig.java:1002) at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:506) at org.apache.catalina.startup.HostConfig.start(HostConfig.java:1315) at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:324) at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:142) at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1061) at org.apache.catalina.core.StandardHost.start(StandardHost.java:840) at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1053) at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:463) at org.apache.catalina.core.StandardService.start(StandardService.java:525) at org.apache.catalina.core.StandardServer.start(StandardServer.java:754) at org.apache.catalina.startup.Catalina.start(Catalina.java:595) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:289) at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:414)上面中错误提到一句很关键的 You cannot use a spring-security-2.0.xsd or spring-security-3.0.xsd or spring-security-3.1.xsd schema with Spring Security 3.2. Please update your schema declarations to the 3.2 schema 也就是说我需要把我的schema升级成 3.2 以上的版本, 然后我在官方网站找到一个最新的一个配置文件:修改成如下就ok了 官方的配置实例 applicationContext-security.xml --new 版本是3.2.4 : <beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> <http> <intercept-url pattern="/**" access="ROLE_USER" /> <form-login /> <logout /> </http>这样就解决了。 然后额外说一下:在上面错误的同时也会报 java.lang.IllegalStateException: BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext。 其实大部分情况就是配置文件不多造成的, 等会 我把完整的项目贴到这儿:
docker run -e TZ="Asia/Shanghai" --name xxxx
version: "2.0"
services:
mysqldb:
image: mysql:5.7.16
container_name: mysql-5.7.16
restart: always
ports:
- "3306:3306"
environment:
- TZ=Asia/Shanghai
- MYSQL_ROOT_PASSWORD=123456
volumes:
- "/data/mysql/data:/var/lib/mysql"
- "./mysqld_charset.cnf:/etc/mysql/conf.d/mysqld_charset.cnf"