|
2009年9月1日
1. java zip 多个文件时,如果先添加了一个excel文件,然后再想添加其他的文件时会出现 steam is closed的错误。这是因为work.write(outputSteam)后,出调用outputSteam.close(),关闭输出流。 解决方法: 将原来的程序: ZipEntry entry = new ZipEntry( "file3.txt" ); zos.putNextEntry( entry ); workbook.write( zos ); zos.closeEntry(); 改为: ZipEntry entry = new ZipEntry( "file3.txt" ); zos.putNextEntry( entry ); workbook.write( new NonCloseableOutputStream( zos ) ); zos.closeEntry(); 其中 NonCloseableOutputStream 定义如下: public class NonCloseableOutputStream extends java.io.FilterOutputStream { public NonCloseableOutputStream(OutputStream out) { super(out); } @Override public void close() throws IOException { flush(); } } 2. 使用binary使得mysql区分大小写 select * from table1 where binary field1 = 'abc';
https://notepad-plus-plus.org/community/topic/13661/plugin-manager-x64-available-submit-your-plugins
move Git Server to a new IP/URL:
you can just edit .git/config and change the URLs there
也可以在git视图中,右键点击项目,选择属性,然后修改url中的地址
autohotkey listary cmder可以split screen,在一个窗口中同时运行数个cmd
官网地址:autohotkey.com ; fill password ^Numpad2:: Send, root{tab}root{enter} Return ^Numpad3:: IfWinExist, ahk_exe OUTLOOK.EXE { WinActivate ahk_exe OUTLOOK.EXE ; Automatically uses the window found above. ; WinMaximize ; same ;Send, Some text.{Enter} msgbox Outlook is running. } Return
<html> <head> <script src="https://unpkg.com/vue/dist/vue.js"></script> <script> window.onload = function () { var app = new Vue({ el: '#app', data: { message: 'Hello Vue!' } }); } </script> </head>
<body> <div id="app"> {{ message }} </div> </body> </html>
String[] splits=someString.split("a,b,c,d", ","); logger.debug( "array: {}", (Object) splits ); 这里要注意的就是要把数组的数据类型强制转换为Object
在windows环境中,可以用如下方法重置root密码 1、先停止mysql数据库 2、保存密码重置sql文件 5.7.6(包括)以后的版本:ALTER USER 'root'@'localhost' IDENTIFIED BY 'MyNewPass'; 5.7.5(包括)以前的版本:SET PASSWORD FOR 'root'@'localhost' = PASSWORD('MyNewPass'); 假设保存到文件: c:\reset.txt 3、以管理员身份打开命令行窗口,运行 C:\> cd "C:\Program Files\MySQL\MySQL Server 5.5\bin" C:\> mysqld --init-file=C:\reset.txt
4、启动后,还不能马上用新密码连接数据库,需要重启mysql数据库
This is a general step that happens when m2e/m2eclipse (Maven integration for Eclipse) is installed, whether projects are actively using it or not. 这是因为m2eclipse(maven插件)要在启动时需要进行的一个步骤。 This step can be disabled through the Eclipse preferences: Window / Preferences / Maven / "Download repository index updates on startup". This option is on the main "Maven" preference page (not a child page). Just uncheck the box to prevent this from happening. 我们可以停止这个动作。方法:Windows -> Preferences -> Maven 取消勾选 Download repository index updates on startup
有好几个java library都可以实现这个功能,但是从pdf提取文本的一个问题是,提取出来的文本没有固定的顺序,不容易比较好的还原其格式。
我的做法是使用pdfclown来进行这项工作。官方网站是:https://pdfclown.org/ 先下载其最新版本。 参考其示例代码:https://pdfclown.org/2010/01/02/upcoming-0-0-8-whats-going-to-be-new/#more-30
使用这段代码,我们不仅可以得到文本的字符串,还能得到文本的页数和相对坐标。 我的思路是先把所有文本的字符串和坐标提取出来。然后排序,排序的顺序是纵坐标,然后横坐标。 这样排序完毕后,就能比较好的解决文本格式问题。
1, 先定义一个input, 做为datepicker的容器。 <input type='text' class="form-control" id="dateTo" name="dateTo" required/>
2, 在后面加上glyphicon, 注意关键是label 中的for的id需要是前面定义的容器的id, 这样点击glyphicon的时候就会触发弹出日期选择框。
<label for="dateTo" class="input-group-addon"><span class="glyphicon glyphicon-time"></span></label>
在日志文件中看到这个错误信息 Cause: java.sql.SQLException: #HY000
后来才知道这是因为数据库中有个别字段要求不能为空, 但是insert语句中没有提供数据,造成了这个错误。 关键是错误信息不明确直观,不容易知道是这个原因
public void afterJFinalStart(){ Configuration config = FreeMarkerRender.getConfiguration(); config.setTemplateUpdateDelayMilliseconds( 2 ); config.setAPIBuiltinEnabled( true ); }
中文版地址 https://angular.cn/
1, call ##002# to cancel "call diversion"
2, call 121600, choose option "2" to cancel "Active call catcher"
1. 格式化XML的插件 可以安装“XML Tools", 安装完毕后,选择 插件->XML Tools->Pretty Print(XML Only - with line breaks)
2. 格式化JSON的插件 可以安装”JSON Viewer", 安装完毕后,选择 插件->JSON Viewer->Format JSON
3. 格式化SQL的插件 可以安装“Poor man's T-Sql Formatter", 选择 插件->Poor man's T-Sql Formatter->Format T-Sql Code
使用的工具
1. Apache HttpClient 2. Firefox + FireBug 3. Burp Suite ( https://portswigger.net/burp ) + Firefox FoxyProxy
Firefox + FireBug 主要用于查看渲染出的页面中的信息(比如:表单项的名称,节点ID等等) Burp Suite 主要用于动态拦截页面的交互,查看Ajax的调用。 HttpClient 用于最后程序的编制。搞清楚了网页交互的过程,就可以自主决定程序需要包含的内容。 在实际网页中,可能需要点开数级菜单,才能最后看到需要的内容。 但是在程序中,可以直接跳到最后一步。
1. 表格文字右对齐
<table>
<tr>
<td><p style="text-align:right;margin:0;padding:0">文字右对齐</p></td>
<td>文字左对齐</td>
</tr>
</table>
2. 表格边缘的margin
需要在表格外再套一个div
<div style="margin:10px"> <table>
...... </table>
</div> 3. btn-toolbar class can put a margin between 2 "pull-right" buttons <div class="row"> <div class="col-md-2"></div> <div class="col-md-8 btn-toolbar"> <input type="submit" class="btn btn-warning pull-right" value="Submit"> <input type="button" id="profilePassBackBtn" class="btn btn-info pull-right" value="Back"> </div> <div class="col-md-2"> </div> </div>
AngularJS 2.0 已经发布了Beta版本,相信正式版不久以后就会发布了。
下面是官网上的新功能介绍:
1. 更快更高效。AngularJS 2 将会比 AnuglarJS 1 快很多。因为它会支持:从远程胳快速加载、离线编译以便于更快启动、以及超快的变动检测和为使滚动更平滑的视图缓存等等。
2. 更加简单清晰。语法将会显得更加自然,易于编写
3. 跨越平台。无论是台式机、手机浏览器、安卓、IOS平台,AngularJS都能提供相应的支持。
4. 无缝从 AngularJS 1 升级到 2
5. 简便的开发。支持各种开发语言,ES5, TypeScript, Dart
6. 全面完备的路由。 方便地映射URL到应用组件,并提供多种高级功能,比如:嵌套和邻接路由,支持卡片栈导航、动画过渡、手机用户延迟加载等等
7. 依赖注入。
8. 旧浏览器的良好支持
9. 动画效果 (仍在开发中)
10. 国际化支持(仍在开发中)
- Go to web project properties.
- Deployment Assembly (Left).
- Add > Select project > Select your lib project > Check "Assemble projects into the WEB-INF/lib folder of the web application" if not checked > Finish.
使用酷狗就可以转换。 右键点击歌曲 ,工具,格式转换。 唯一要注意的是要先登录。
今天把commons dbcp 和 pool都升级到2.x, 结果发现不能正常的工作,卡在new BasicDataSource()上了. 后来才发现原因是因为没有加入commons-logging的jar文件 几个注意点: 1. commons dbcp2.x 和 commons pool需要同时升到2.x 2. dbcp 2.x要运行在java 7以上 3. mysql connector要5.1.11以上 4. 需要有commons-logging的包,我使用的是slf4j, 就需要加一个jcl-over-slf4j
Error com.jcraft.jsch.JSchException: The cipher 'aes256-cbc' is required, but it is not available. or
Caused by: java.security.InvalidKeyException: Illegal key size
我在网上搜索了一下如何使用Selenium下载文件,其中确实有几篇文件介绍了实现的方法。 但是其主要思想都是使用httpClient或者URL获得InputStream, 然后保存到文件中。 但是,其中的问题是用户登录的Session不能维持。
我发现了一个简单的方法。 直接使用WebDriver.get, 示例如下:
webDriver.get("https://website.com/login"); WebElement element = driver.findElement( By.id( "userID" ) ); element.sendKeys( "user01" );
element = driver.findElement( By.id( "passwd" ) ); element.sendKeys( "password" );
element = driver.findElement( By.name( "Login" ) ); element.submit();
webDriver.get("https://website.cm/download.do?start=xx&end=yy"); String source = webDriver.getPageSource();
这个source就是我们想保存的要下载的内容。 只要把这个String写到一个文件中,就实现了文件下载的目的
摘要: 在我的上一篇文章中介绍了如何进行GPG加密解密。
加密解密的基本操作流程是,用户使用公钥对明文进行加密,解密方使用私钥对密文进行解密。
在实际应用中,除了加密保证文本内容不泄露外,同时还要考虑能够验证密文发送方的身份,比较普遍使用的方法就是签名。
本文主要对具体的方法进行介绍并附上源代码。 阅读全文
Java程序中访问拥有全部读写权限的目录相对比较简单,和普通的目录没有什么差别。 但是要访问一个需要用户和密码验证的目录就需要一点点小技巧了。 这里介绍一个开源的库能够比较容易的实现这一需求。 1。 下载库文件: https://jcifs.samba.org/ 下载的zip文件中, 不仅包含了jar文件,还有文档和示例。 2。拷贝jcif-1.3.18.jar到类路径中。 3。代码示例: 1 String user = "your_user_name"; 2 String pass ="your_pass_word"; 3 4 String sharedFolder="shared"; 5 String path="smb://ip_address/"+sharedFolder+"/test.txt"; 6 NtlmPasswordAuthentication auth = new NtlmPasswordAuthentication("",user, pass); 7 SmbFile smbFile = new SmbFile(path,auth); 8 SmbFileOutputStream smbfos = new SmbFileOutputStream(smbFile); 9 smbfos.write("testing .and writing to a file".getBytes()); 10 System.out.println("completed nice !"); 说明: 如果有一个共享目录,比如: \\192.168.1.2\testdir\ 那么smb的路径就是:smb://192.168.1.2/testdir/ NtlmPasswordAuthentication需要三个参数, 第一个是域名,没有的话,填null, 第二个是用户名,第三个是密码
得到SmbFile之后,操作就和java.io.File基本一样了。 另外还有一些功能比如: SmbFile.copyTo SmbFile.renameTo 等等
先将my.default.ini改名为my.ini放到bin目录
命令行执行: mysqld --initialize --user=mysql --console
先执行以上命令, 生成库. 注意有个临时密码, 要记下来. 安装服务:mysqld.exe --install MySql5.7 --defaults-file=c:\mysql\mysql5.7\my.ini
然后启动服务.
然后再命令行: mysql -uroot -p 输入密码, 再输入: set password = password('root') 改密码成功, 然后就可以操作了.
如果只是在beforeSubmit()中 调用$('#fieldname').val(2)是不能成功修改表单的值的。 因为此时ajaxForm已经把表单中所有的内容存储在arr之中了。 $('#form1').ajaxForm({ beforeSubmit: function(arr){ for ( var i = 0; i < arr.length; i ++ ) { if ( arr[i].name == "fieldName1" ) { arr[i].value = '新的值'; } } } }); 需要使用这种方式进行修改。
今天在运行myeclipse的时候,突然报nullPointerException. 具体的错误信息如下: Message: Errors running builder ‘DeploymentBuilder’ on project XXX’. Exception Stack Trace java.lang.NullPointerException 解决方法: 1. Shut down the workspace. 2. Delete the file com.genuitec.eclipse.ast.deploy.core.prefs which is located at <workspace dir>/.metadata/.plugins/org.eclipse.core.runtime/.settings/com.genuitec.eclipse.ast.deploy.core.prefs 3. Start the IDE.
ipconfig /flushdns ipconfig /registerdns netsh winsock reset
重新启动电脑。
今天下载了Apache James 3.0 Beta 5, 文件名:james-server-app-3.0.0-beta5-20150627.102412-1076-app.zip 解压,运行run.bat 然后,注册domain james-cli --host localhost adddomain example.com 添加用户 james-cli.bat --host localhost adduser test@example.com password 然后测试发送邮件,客户端显示发送成功,但是james服务器报错,找不到MimeConfig的无参数构造函数。 解决方法: 使用旧的mime4j的jar包替换james 3.0 beta5中自带的最新包。 beta5中自带的是0.8.0版,apache网站中可以下载到0.7.2 下载apache-mime4j-0.7.2-bin.zip, 将其中的apache-mime4j-core-0.7.2.jar, apache-mime4j-dom-0.7.2.jar复制到james\lib目录, 并将其更名覆盖原有的 apache-mime4j-core-0.8.0-20150617.024907-738.jar apache-mime4j-dom-0.8.0-20150617.024927-735.jar 重新启动james, 发送邮件, 成功。
摘要: 解压/生成有密码保护的压缩文件, 研发过程中,作者研究了压缩文件格式文档: http://www.pkware.com/documents/casestudies/APPNOTE.TXT,并且参考了7-zip的实现。
阅读全文
摘要: 花了两天时间终于把windows10安装好了,以下是我的一些个人的体会
阅读全文
在JfinalConfig的继承类中, configConstant() 需要设置me.setDevMode(true); 1. 只有在DevMode下,才能禁止freeMarker的缓存。 Configuration config = FreeMarkerRender.getConfiguration(); config.setTemplateUpdateDelayMilliseconds(0); 才会生效
2. 这时才会有JFinal Action Report日志输出
本文将简单介绍如何使用PowerMock和Mockito来mock
1. 构造函数
2. 静态函数
3. 枚举实现的单例
4. 选择参数值做为函数的返回值
5. 在调用mock出来的方法中,改变方法参数的值
一点简要说明:Mockito其实已经可以满足大部分的需求,但是它的实现机制是使用cglib来动态创建接口的类的实例。但是这种实现方式不能用于构造函数和静态函数,因为那需要使用类的字节码(比如使用javassist). 所以我们才需要结合使用PowerMock.
1. mock构造函数, 如果有代码没有使用DI注入依赖实例,在单元测试中可以使用PowerMock来模拟创建对象。
注意的开始两行的2个注解 @RunWith 和 @PrepareForTest
@RunWith比较简单,后面始终是PowerMockRunner.class
@PrepareForText后面需要加的是调用构造函数的类名,而不是有构造函数的类本身。
在下面的例子中,我们要测试的类是:Helper, 在Helper类中调用了Somthing类的构造函数来创建实例。
@RunWith(PowerMockRunner.class)
@PrepareForTest(Helper.class)
public class HelperTest {
@Mock
private Something mockSomething;
@InjectMocks
private Helper helper;
@Test
public void doSomething() throws Exception {
String argument = "arg";
PowerMockito.whenNew(Something.class).withArguments(argument).thenReturn(mockSomething);
// 调用需要测试方法
helper.doSomething(argument);
// 进行验证
verify(mockSomething).doIt();
}
}
public class Helper {
public void doSomething(String arg) {
Something something = new Something(arg);
something.doit();
}
}
2,mock 静态函数, 单例模式就是一个典型的会调用静态函数的例子。 注意要点与mock构造函数相同。
class ClassWithStatics {
public static String getString() {
return "String";
}
public static int getInt() {
return 1;
}
}
@RunWith(PowerMockRunner.class)
@PrepareForTest(ClassWithStatics.class)
public class StubJustOneStatic {
@Test
public void test() {
PowerMockito.mockStatic(ClassWithStatics.class);
when(ClassWithStatics.getString()).thenReturn("Hello!");
System.out.println("String: " + ClassWithStatics.getString());
System.out.println("Int: " + ClassWithStatics.getInt());
}
}
3。mock枚举实现的单例
SingletonObject.java
public enum SingletonObject { INSTANCE; private int num; protected void setNum(int num) { this.num = num; } public int getNum() { return num; } }
SingletonConsumer.java
public class SingletonConsumer {
public String consumeSingletonObject() {
return String.valueOf(SingletonObject.INSTANCE.getNum());
} }
SingletonConsumerTest.java
@RunWith(PowerMockRunner.class) @PrepareForTest({SingletonObject.class}) public class SingletonConsumerTest { @Test public void testConsumeSingletonObject() throws Exception { SingletonObject mockInstance = mock(SingletonObject.class); Whitebox.setInternalState(SingletonObject.class, "INSTANCE", mockInstance); when(mockInstance.getNum()).thenReturn(42); assertEquals("42", new SingletonConsumer().consumeSingletonObject()); } }
4。返回参数值做为函数返回值。
mockito 1.9.5之后,提供一个方便的方法来实现这个需要,在这之前可以使用一个匿名函数来返回一个answer来实现。
when(myMock.myFunction(anyString())).then(returnsFirstArg());
其中returnsFirstArg()是org.mockito.AdditionalAnswers中的一个静态方法。
在这个类中还有其他的一些类似方法
returnsSecondArg()
returnsLastArg()
ReturnsArgumentAt(int position)
5. 在调用mock出来的方法中,改变方法参数的值
when( myMock.someMethod( any( List.class ) ) ).thenAnswer( ( new Answer<Void>() {
@Override
public Void answer( InvocationOnMock invocation )
throws Throwable {
Object[] args = invocation.getArguments();
List arg1 = (List)args[0];
arg1.add("12345");
return null;
}
} ) );
Verifying with generic parameters
verify(someService).process(Matchers.<Collection<Person>>any());
verify(adunoMasterBaseProcessor).processBinFiles( anyListOf(File.class) );
Oracle提供的JDK其实已经自带一定程度的热加载功能,但是如果你修改了类名,方法名,或者添加了新类,新方法的话。 Tomcat都需要重新启动来使得刚才的更改生效。 而JRebel和springloaded都能有效地解决这个问题。其中springloaded是开源软件,可以免费使用,尤其难得。 其主页:https://github.com/spring-projects/spring-loaded 在官方页面的简单介绍中,作者只讲述了如何在java程序中应用springloaded,而没有说明如何在tomcat中进行配置。 本文将简要进行介绍。 1,下载springloaded到本地目录,比如:c:\temp\springloaded-1.2.3.RELEASE.jar 2. 修改tomcat的应用,禁止tomcat自己的热加载,方法是在META-INF目录下创建context.xml文件,里面包含如下语句,关键便是其中设置reloadable为false <?xml version="1.0" encoding="UTF-8"?> <Context antiResourceLocking="false" privileged="true" useHttpOnly="true" reloadable="false" /> 3.在运行环境中添加springloaded的jar文件,在eclipse中右键点击项目,run as->run configuration 在弹出的窗口中,选择Arguments标签,在vm arguments的末尾添加: -javaagent:C:\temp\springloaded-1.2.3.RELEASE.jar -noverify 点击应用按钮。 以上便完成了所有的配置,步骤并不复杂。
java wrapper是一个可以用于将java应用程序包装成windows服务的工具。 并且可以通过简单的配置来允许使用visualVM进行监控。 配置方法: 在wrapper.conf中添加如下3行 wrapper.java.additional.1=-Dcom.sun.management.jmxremote.port=9898 #这里的端口号可以自行选择。 wrapper.java.additional.2=-Dcom.sun.management.jmxremote.ssl=false wrapper.java.additional.3=-Dcom.sun.management.jmxremote.authenticate=false 修改完毕保存后重新启动服务。 打开visualVM, 在菜单中选择 file->Add JMX Connection。 在弹出窗口中,connection一项中输入: localhost:9898 即可。 此配置对于jconsole也同样有效。
在一些历史遗留代码中,会用到java.util.logging. 如果在新的项目中引用了这些代码,而又不希望去一个一个的修改原来的代码。 可以使用slf4j提供的类来转接这部分的日志输出。 方法: 1、类路径中添加 slf4j-api-1.7.10.jar jul-to-slf4j.1.7.10.jar ( 用于将java.util.logging的日志桥接到slf4j中) logback-core.1.1.2.jar logback-classic-1.1.2.jar 2、在代码中添加: // Optionally remove existing handlers attached to j.u.l root logger SLF4JBridgeHandler.removeHandlersForRootLogger(); // (since SLF4J 1.6.5)
// add SLF4JBridgeHandler to j.u.l's root logger, should be done once during // the initialization phase of your application SLF4JBridgeHandler.install(); 注意事项: 1、这个桥接可以会造成性能问题。 和其他的桥接实现(比如:log4j, commons logging)不同,这个模块并不真正的完全替代java.util.logging类,因为这个java.util.logging是java自带的。 所以只是把原来的日志对象进行了转换,简单的说,这个转换过程是有开销的。 关键在于,不管日志语句有没有根据日志级别被关闭,这个转换无法避免。 2、不能在类路径中放入 slf4j-jkd14.jar jul-toslf4j.jar
1. Text Editor: Notepad++/Syncplify.me Notepad! 2. Browser: Chrome/Firefox 3. 文件管理: XYplorer Lite/Explorer++/Q-Dir 4. Mind map: XMind Free 5. Video player: PotPlayer 6. Music player: Kugou 7. Mysql client: HeidiSql 8. PDF reader: Foxit Reader 9. File/Folder synchronize : FreeFileSync 10. MP3 tools: Audacity/MP3 Gain 11. Zip: 7-zip 12. Partition Management: EaseUS Partition Master Free / MiniTool Free Partition Manager 13. Data Recovery: EaseUS Data Recovery Wizard Free / MiniTool Free Data Recovery 14. PDF Printer: PDF reDirect v2 15. 个人信息管理: EssentialPIM Free Edition 16. 远程登录: Terminals 17. 文本比较合并: winmerge 18. (s)FTP client: WinSCP 19. 图像处理: GIMP
Ember 是一个旨在创建大型 web应用的JavaScript框架,它消除了样板(boilerplate)并提供了标准的应用程序架构。
Manning: Ember.js in action 第一章
Manning: Ember.js in action 第五章
先给一个例子: $http. get('/remote/item' ). then(function(response) { console.log('成功。'); }, function(errResponse) { console. error('出错.' ); });
一。介绍Promise 在这个例子中,$http.get()函数返回了一个Promise对象, 有了这个对象,我们才能很方便地直接在后面添加then函数的定义。 Promise对象在AngularJS中是一个非常重要的存在。它提供了强大的功能和便利性。
1。异步性 从定义的语法上看,操作似乎是同步的,但是Promise的工作其实是异步的,只有在服务端返回数据后,后续的函数才会被调用。这是一个事件驱动,非阻塞式的框架。
2。它避免了其它框架的嵌套回调函数的缺点。 -所有异步任务都会返回一个Promise对象 -每个Promise对象都有一个then函数,then函数有两个参数,分别是成功处理函数和失败处理函数 -失败处理函数和成功处理函数都只会在异步处理完成后被调用一次 -then函数也会返回Promise对象,这样,我们可以把多个函数串连起来成为一个函数链 -成功处理函数和失败处理函数的返回值可以被传递到函数链下一个的函数中 -如果在成功(或者失败)处理函数中,又开始了一个异步调用,那么函数链中的函数将会在这个异步调用结束后才开始
二。异步链式调用的后续处理 假如我们定义了如下的函数链: $http.get('/item').then(s1, e1).then(s2, e2).then(s3, e3); 我们如何自主的根据函数链中每个函数的运行结果,决定触发后续函数的成功处理函数或者失败处理函数呢? 比如说,在s1处理过程中,发生问题,于是我们触发了e2, 但是在e2处理完后,我们又想触发s3. AnguarJS提供了$q来满足这样的需求。 如果我们想触发函数链中下一个函数的成功处理,我们只需要最后给出一个返回值,有了返回值,AngularJS会认为函数执行正确,自动调用下一个函数中的成功处理 如果想触发失败处理,那么可以简单地返回$q.reject(data),这样就会触发下一个函数的失败处理
在前文(http://www.blogjava.net/usherlight/archive/2015/02/01/422633.html)中我们曾经介绍过,定义controller时,需要2个参数,第一个参数是controller的名称,第二个参数是一个数组,数组的最后一个元素将是controller的函数,前面的参数是controller的依赖项。我们现在就来仔细分析一下其中的具体过程。 先给一个例子: angular. module('notesApp' , []) . controller('MainCtrl' , ['$log' , function($log) { var self = this; self. logStuff = function() { $log. log('The button was pressed' ); }; }]) 在这个例子中可以看到,我们在第一个参数中用字符串(服务名称)添加了一个依赖项。当我们通过字符串声明了这一个服务之后,我们就可以把它当作一个变量注入到函数中。AngularJS会自动查找字符串名称对应的服务名,按照顺序将其注入到函数中。 myModule.controller("MainCtrl", ["$log", "$window", function($l, $w) {}]); 在这个例子中,$log, $windows是AngularJS自带的两个服务,在数组中通过名称声明后,会被注入到函数的两个参数中。 比较常用的AngularJS自带的服务有:$window, $location, $http等
从上面的例子中可以看出,AngularJS的设计思想就是不要在函数中自己去实例化或者通过其它途径来获取服务的实例,而是声明需要的对象,由AngularJS来注入具体的实例。
创建自己的服务 什么时候应该创建服务,而不是controller呢? 1。 需要重用的时候 2。需要保留应用级的状态。这是非常重要的一点,controller是会不断地被创建和销毁的,如果需要保存应用级的状态,就需要使用service 3。和页面显示无关 4。需要和第三方服务整合 5。缓存
服务是会被延迟加载的,也就是说只有在第一次被引用的时候,才会被创建。 服务将会被定义一次,也只会被实例化一次。
摘要: 默认情况下,每隔一秒种,SpringLoaded就会扫描类路径,自动加载改变过的类, 而不需要重新启动应用 阅读全文
07. ng-repeart a. 在循环map的时候,会自动根据键值进行排序。 b. 一些自带的变量,$first(是否是第一个), $last(是否是最后一个), $middle(是否是中间的), $index(下标,根据键值排序后的下标), $even, $odd 08. 自己定义新变量时不要使用$$开头。 09. 可以使用track-by表达式来优化对DOM的操作,对DOM对象使用从数据库取得的ID来进行标记,这样的话,当我们重复多次从数据库中取出相同的数据的时候,DOM对象就能够被重用。 10. 数据双向绑定的好处 a. 如果我们想改变页面Form中的数值,我们不需要在Javascript中,根据ID或者名称来查找相应的Form控件,只需要改变Controller变量的值,不需要JQuery的Selector,也不需要findElementByID b. 如果我们想在javascript中获取Form控件的值,在控件的变量中就能直接获得。 11. 使用ng-submit比在button上使用ng-click要好一些。HTML的表单的提交有多种方式,比如在输入域中按回车键就会触发ng-submit,而不会触发button的ng-click事件。 12. 在ng-model中,可以直接引用一个对象,比如:<input type="text" ng-model="ctrl.user.name">,而不需要事先在model中以self.user={}定义。在AngularJS中,使用了ng-model的话,AngularJS在初始化数据绑定的时候,自动创建其中的对象和键值。在刚才的例子中,一旦用户开始在输入域中键入第一个字母,用户user就会被自动创建。 13. 推荐使用将相关数据集中到一个对象的方式来进行数据绑定,比如,用户名和密码,推荐使用: <input type="text" ng-model="ctrl.user.name"> <input type="text" ng-model="ctrl.user.password"> 而不是: <input type="text" ng-model="ctrl.name"> <input type="text" ng-model="ctrl.password">
1. AngularJS的module函数有两种用法,
a. 定义一个module, 需要传入2个参数,module('moduleName', []), 第一个参数是新的module名称,第二个参数是新module所依赖的module数组。
b. 载入一个module, 只需要1个参数,module('moduleName'), 唯一的一个参数指定要载入的module名称。
2. 使用controller函数来定义一个控制器(controller), 用ng-controller将控制器绑定到具体的HTML组件上。定义控制器的controller函数也需要2个参数,第一个是控制器名称,第二个参数同样也是一个数组,数组的最后一个元素就是controller本身的函数,前面的元素用字符串的形式指定其需要的依赖项。如果没有依赖项,那就只需要定义函数。比如:
angular.module('app1', [])
.controller('mainControl', [function() {
console.log('controller created.');
}]);
3. 在controller函数中用var定义的局部变量,在HTML中是不可见的。
4. 推荐在controller函数中尽量避免直接引用this, 比较好的做法是使用代理。原因是一个函数中的this关键词在被外部调用的时候,是会被覆盖掉的。这样的话,在函数内部和外部的this会是完全不同两个对象。
代理用法示例:
angular.module('app1', [])
.controller('mainControl', [function() {
var self = this;
self.message = 'Hello world';
self.changeMessage = function() {
self.message = 'Goodbye.';
};
}]);
5. ng-bind与双大括号的区别, ng-bind和{{}}可以说基本上是可以互相替换的,但是也有区别。区别在于:AngularJS在启动的时候就会执行ng-bind, 而{{}}的替换时间会稍晚一些。有可能发现页面在加载的时候,双括号被一闪而过地替换掉(只在页面初次加载的时候发生)。但是ng-bind就没有这个问题。
6. ng-cloak可以用于解决双括号闪现的问题。
1. HTML页面的加载,这会触发加载页面包含的所有JS (包括 AngularJS) 2. AngularJS启动,搜寻所有的指令(directive) 3. 找到ng-app,搜寻其指定的模块(Module),并将其附加到ng-app所在的组件上。 4. AnguarJS遍历所有的子组件,查找指令和bind命令 5. 每次发现ng-controller或者ng-repeart的时候,它会创建一个作用域(scope),这个作用域就是组件的上下文。作用域指明了每个DOM组件对函数、变量的访问权。 6. AngularJS然后会添加对变量的监听器,并监控每个变量的当前值。一旦值发生变化,AngularJS会更新其在页面上的显示。 7. AngularJS优化了检查变量的算法,它只会在某些特殊的事件触发时,才会去检查数据的更新,而不是简单地在后台不停地轮询。
Java虚拟机规范规定JVM的内存分为了好几块,比如堆,栈,程序计数器,方法区等,而Hotspot jvm的实现中,将堆内存分为了三部分,新生代,老年代,持久带,其中持久带实现了规范中规定的方法区,而内存模型中不同的部分都会出现相应的OOM错误,接下来我们就分开来讨论一下。 栈溢出(StackOverflowError) 栈溢出抛出java.lang.StackOverflowError错误,出现此种情况是因为方法运行的时候栈的深度超过了虚拟机容许的最大深度所致。 出现这种情况,一般情况下是程序错误所致的,比如写了一个死递归,就有可能造成此种情况。 下面我们通过一段代码来模拟一下此种情况的内存溢出。 - import java.util.*;
- import java.lang.*;
- public class OOMTest{
-
- public void stackOverFlowMethod(){
- stackOverFlowMethod();
- }
-
- public static void main(String... args){
- OOMTest oom = new OOMTest();
- oom.stackOverFlowMethod();
- }
-
- }
运行上面的代码,会抛出如下的异常: 引用 Exception in thread "main" java.lang.StackOverflowError at OOMTest.stackOverFlowMethod(OOMTest.java:6) 堆溢出(OutOfMemoryError:java heap space) 堆内存溢出的时候,虚拟机会抛出java.lang.OutOfMemoryError:java heap space,出现此种情况的时候,我们需要根据内存溢出的时候产生的dump文件来具体分析(需要增加-XX:+HeapDumpOnOutOfMemoryErrorjvm启动参数)。出现此种问题的时候有可能是内存泄露,也有可能是内存溢出了。 如果内存泄露,我们要找出泄露的对象是怎么被GC ROOT引用起来,然后通过引用链来具体分析泄露的原因。 如果出现了内存溢出问题,这往往是程序本生需要的内存大于了我们给虚拟机配置的内存,这种情况下,我们可以采用调大-Xmx来解决这种问题。 下面我们通过如下的代码来演示一下此种情况的溢出: - import java.util.*;
- import java.lang.*;
- public class OOMTest{
-
- public static void main(String... args){
- List<byte[]> buffer = new ArrayList<byte[]>();
- buffer.add(new byte[10*1024*1024]);
- }
-
- }
我们通过如下的命令运行上面的代码: - java -verbose:gc -Xmn10M -Xms20M -Xmx20M -XX:+PrintGC OOMTest
程序输入如下的信息: 引用 [GC 1180K->366K(19456K), 0.0037311 secs] [Full GC 366K->330K(19456K), 0.0098740 secs] [Full GC 330K->292K(19456K), 0.0090244 secs] Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at OOMTest.main(OOMTest.java:7) 从运行结果可以看出,JVM进行了一次Minor gc和两次的Major gc,从Major gc的输出可以看出,gc以后old区使用率为134K,而字节数组为10M,加起来大于了old generation的空间,所以抛出了异常,如果调整-Xms21M,-Xmx21M,那么就不会触发gc操作也不会出现异常了。 通过上面的实验其实也从侧面验证了一个结论:当对象大于新生代剩余内存的时候,将直接放入老年代,当老年代剩余内存还是无法放下的时候,出发垃圾收集,收集后还是不能放下就会抛出内存溢出异常了 持久带溢出(OutOfMemoryError: PermGen space) 我们知道Hotspot jvm通过持久带实现了Java虚拟机规范中的方法区,而运行时的常量池就是保存在方法区中的,因此持久带溢出有可能是运行时常量池溢出,也有可能是方法区中保存的class对象没有被及时回收掉或者class信息占用的内存超过了我们配置。当持久带溢出的时候抛出java.lang.OutOfMemoryError: PermGen space。 我在工作可能在如下几种场景下出现此问题。 1.使用一些应用服务器的热部署的时候,我们就会遇到热部署几次以后发现内存溢出了,这种情况就是因为每次热部署的后,原来的class没有被卸载掉。 2.如果应用程序本身比较大,涉及的类库比较多,但是我们分配给持久带的内存(通过-XX:PermSize和-XX:MaxPermSize来设置)比较小的时候也可能出现此种问题。 3.一些第三方框架,比如spring,hibernate都通过字节码生成技术(比如CGLib)来实现一些增强的功能,这种情况可能需要更大的方法区来存储动态生成的Class文件。 我们知道Java中字符串常量是放在常量池中的,String.intern()这个方法运行的时候,会检查常量池中是否存和本字符串相等的对象,如果存在直接返回对常量池中对象的引用,不存在的话,先把此字符串加入常量池,然后再返回字符串的引用。那么我们就可以通过String.intern方法来模拟一下运行时常量区的溢出.下面我们通过如下的代码来模拟此种情况: - import java.util.*;
- import java.lang.*;
- public class OOMTest{
-
- public static void main(String... args){
- List<String> list = new ArrayList<String>();
- while(true){
- list.add(UUID.randomUUID().toString().intern());
- }
- }
-
- }
我们通过如下的命令运行上面代码: java -verbose:gc -Xmn5M -Xms10M -Xmx10M -XX:MaxPermSize=1M -XX:+PrintGC OOMTest 运行后的输入如下图所示: 引用 Exception in thread "main" java.lang.OutOfMemoryError: PermGen space at java.lang.String.intern(Native Method) at OOMTest.main(OOMTest.java:8) 通过上面的代码,我们成功模拟了运行时常量池溢出的情况,从输出中的PermGen space可以看出确实是持久带发生了溢出,这也验证了,我们前面说的Hotspot jvm通过持久带来实现方法区的说法。 OutOfMemoryError:unable to create native thread 最后我们在来看看java.lang.OutOfMemoryError:unable to create natvie thread这种错误。 出现这种情况的时候,一般是下面两种情况导致的: 1.程序创建的线程数超过了操作系统的限制。对于Linux系统,我们可以通过ulimit -u来查看此限制。 给虚拟机分配的内存过大,导致创建线程的时候需要的native内存太少。我们都知道操作系统对每个进程的内存是有限制的,我们启动Jvm,相当于启动了一个进程,假如我们一个进程占用了4G的内存,那么通过下面的公式计算出来的剩余内存就是建立线程栈的时候可以用的内存。 线程栈总可用内存=4G-(-Xmx的值)- (-XX:MaxPermSize的值)- 程序计数器占用的内存 通过上面的公式我们可以看出,-Xmx 和 MaxPermSize的值越大,那么留给线程栈可用的空间就越小,在-Xss参数配置的栈容量不变的情况下,可以创建的线程数也就越小。因此如果是因为这种情况导致的unable to create native thread,那么要么我们增大进程所占用的总内存,或者减少-Xmx或者-Xss来达到创建更多线程的目的。
现在的显示屏都在飚像素,商家的宣传一个比一个噱头大,什么 Retina、4K、8K 这种名词一个接一个的出来, 这些到底都是啥意思? 首先,Retina 和 4K 以及 8K 并不是同一层面的定义。屏幕一般是以像素点做单位的,4K 和 8K 就是直接限定了像素点的多少,而 Retina 则是没有硬性的规范。Retina 屏幕的概念最早由苹果公司执行长史蒂夫·乔布斯(Steve Jobs)于 WWDC2010 发布 iPhone 4 时提出的。 定义是:要求在正常观看距离下,足以使人肉眼无法分辨其中的单独像素。因此它并没有限定像素值多少。
4K 就是水平方向每一行的像素值达到或是接近 1024 的 4 倍,8K 就是达到或接近 8 倍。 以此为标准,4K 一般图像就是指 4096*2160 的分辨率。当然,这也不是硬性要求,像市场上很多 4K 屏幕其实是 3840*2160 或是 3656*2664,这些都是 4K 图像分辨率的范畴。 8K 就是分辨率在 7680*4320 左右。
顺便说一下,720p 则是指竖直方向的像素点达到 720 个,1080p 则是 1080 个,“P”是逐行扫描的意思
问:那到底 Retina 和 4K 或是 Retina 和 8K 哪个更清楚呢? 答:不一定,二者不能平行比较。 因为 4K 和 8K 是限定了像素点的多少,而 Retina 是要求正常距离看不到像素点。 举个例子:如果放到正常的 42 寸屏幕上,4K 和 8K 在正常距离观看下都看不到像素点,那么两者都可以被称作“Retina 屏幕”。
可是如果给你一台 500 寸的巨大屏幕,那么即便是 8K 也会到处是马赛克,这时 Retina 观感依然是高清无像素点,必然比 8K 和 4K 清楚的多。
Last_SQL_Error: Error 'Lock wait timeout exceeded; try restarting transaction' on query. Default database: 'test'. Query: 'DELETE FROM table1 WHERE id = 361' 1 row in set (0.00 sec)
solution: restart slave;
stop slave; start slave;
1. server.xml 在<engine>中添加 <Realm className="org.apache.catalina.realm.MemoryRealm" /> 2. tomcat-user.xml <role rolename="manager"/> <role rolename="manager-gui"/>
<user username="admin" password="tomcat" roles="manager"/>
1. 自动扫描配置文件改动
<configuration scan="true" scanPeriod="30 seconds">
....
</configuration
2. 日志每天归档,同时目录名包含相应的年份和月份
<fileNamePattern>F:\Programs\GlobalPos\GatewayCiti\logs\%d{yyyy/MM,aux}\G%d{dd}-%i.log</fileNamePattern>
注意其中aux的使用,在fileNamePatter中如果出现多个%d的情况下,只能有一个为主配置,其他都需要使用aux标记为附属配置
其中的%i请参看下节的介绍
3. 文件同时根据日期和大小滚动创建
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- rollover daily -->
<!--
<fileNamePattern>F:\Programs\GlobalPos\NetReport\logs\Portal-%d{yyyyMMdd}.log</fileNamePattern>
-->
<!-- Size and time based archiving -->
<fileNamePattern>D:\logs\%d{yyyy/MM,aux}\L%d{dd}-%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
fileNamePattern在上一节已经介绍,这里主要介绍timeBasedFileNamingAndTriggeringPolicy,此处配置对文件大小的限定,由fileNamePattern的%i在确定下标在文件名中的位置
此示例产生的日志文件将会是:
D:\logs\2015\01\L05-0.log 如果该文件大于100M,就会生成D:\logs\2015\01\L05-1.log
OpenPGP 号称是世界上使用最广泛的邮件加密标准. OpenPGP is the most widely used email encryption standard in the world. ( http://www.openpgp.org/ ) 这篇例子介绍如何使用这个标准进行文件的加密解密 (https://www.bouncycastle.org/latest_releases.html, 需要下载: bcprov-jdk15on-151.jar, bcpg-jdk15on-151.jar). 主要是使用bouncycastle提供的OpenPGP的库来完成这个功能,参照了其提供的示例程序,进行了部分改动 ( Bouncy Castle 是一种用于 Java 平台的开放源码的轻量级密码术包。它支持大量的密码术算法,并提供 JCE 1.2.1 的实现。因为 Bouncy Castle 被设计成轻量级的,所以从 J2SE 1.4 到 J2ME(包括 MIDP)平台,它都可以运行。它是在 MIDP 上运行的唯一完整的密码术包。)1. 添加循环遍历来查找第一个可用的message 2. 需要注意的是在main函数中的, 如果不添加这一句的话 Security.addProvider(new BouncyCastleProvider()); 程序运行中会报错:No such Provider "BC" 3. 错误Exception in thread "main" java.security.InvalidKeyException: Illegal key size or default parameters , 这是因为java缺省的库支持的key长度比较短,需要到oracle的网站上去下载一个支持更长key的库覆盖原有的库文件 <JAVA_HOME>/lib/securty/ 目录下的两个jar文件 local_policy.jar and US_export_policy.jar 搜索这个文件: Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files
package org.bouncycastle.openpgp.examples;
import java.io.BufferedInputStream; import java.io.BufferedOutputStream; 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.security.NoSuchProviderException; import java.security.SecureRandom; import java.security.Security; import java.util.Iterator;
import org.bouncycastle.bcpg.ArmoredOutputStream; import org.bouncycastle.bcpg.CompressionAlgorithmTags; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.openpgp.PGPCompressedData; import org.bouncycastle.openpgp.PGPEncryptedData; import org.bouncycastle.openpgp.PGPEncryptedDataGenerator; import org.bouncycastle.openpgp.PGPEncryptedDataList; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPLiteralData; import org.bouncycastle.openpgp.PGPOnePassSignatureList; import org.bouncycastle.openpgp.PGPPrivateKey; import org.bouncycastle.openpgp.PGPPublicKey; import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData; import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; import org.bouncycastle.openpgp.PGPSignatureList; import org.bouncycastle.openpgp.PGPUtil; import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory; import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; import org.bouncycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder; import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder; import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator; import org.bouncycastle.util.io.Streams;
/** * A simple utility class that encrypts/decrypts public key based * encryption files. * <p> * To encrypt a file: KeyBasedFileProcessor -e [-a|-ai] fileName publicKeyFile.<br> * If -a is specified the output file will be "ascii-armored". * If -i is specified the output file will be have integrity checking added. * <p> * To decrypt: KeyBasedFileProcessor -d fileName secretKeyFile passPhrase. * <p> * Note 1: this example will silently overwrite files, nor does it pay any attention to * the specification of "_CONSOLE" in the filename. It also expects that a single pass phrase * will have been used. * <p> * Note 2: if an empty file name has been specified in the literal data object contained in the * encrypted packet a file with the name filename.out will be generated in the current working directory. */ public class KeyBasedFileProcessor { private static void decryptFile( String inputFileName, String keyFileName, char[] passwd, String defaultFileName) throws IOException, NoSuchProviderException { InputStream in = new BufferedInputStream(new FileInputStream(inputFileName)); InputStream keyIn = new BufferedInputStream(new FileInputStream(keyFileName)); decryptFile(in, keyIn, passwd, defaultFileName); keyIn.close(); in.close(); }
/** * decrypt the passed in message stream */ private static void decryptFile( InputStream in, InputStream keyIn, char[] passwd, String defaultFileName) throws IOException, NoSuchProviderException { in = PGPUtil.getDecoderStream(in); try { JcaPGPObjectFactory pgpF = new JcaPGPObjectFactory(in); PGPEncryptedDataList enc;
Object o = pgpF.nextObject(); // // the first object might be a PGP marker packet. // if (o instanceof PGPEncryptedDataList) { enc = (PGPEncryptedDataList)o; } else { enc = (PGPEncryptedDataList)pgpF.nextObject(); } // // find the secret key // Iterator it = enc.getEncryptedDataObjects(); PGPPrivateKey sKey = null; PGPPublicKeyEncryptedData pbe = null; PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection( PGPUtil.getDecoderStream(keyIn), new JcaKeyFingerprintCalculator());
while (sKey == null && it.hasNext()) { pbe = (PGPPublicKeyEncryptedData)it.next(); sKey = PGPExampleUtil.findSecretKey(pgpSec, pbe.getKeyID(), passwd); } if (sKey == null) { throw new IllegalArgumentException("secret key for message not found."); } InputStream clear = pbe.getDataStream(new JcePublicKeyDataDecryptorFactoryBuilder().setProvider("BC").build(sKey)); JcaPGPObjectFactory plainFact = new JcaPGPObjectFactory(clear); Object message = plainFact.nextObject(); while ( true ) { if (message instanceof PGPCompressedData) { PGPCompressedData cData = (PGPCompressedData)message; JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(cData.getDataStream()); message = pgpFact.nextObject(); } if (message instanceof PGPLiteralData) { PGPLiteralData ld = (PGPLiteralData)message;
String outFileName = ld.getFileName(); if (outFileName.length() == 0) { outFileName = defaultFileName; }
InputStream unc = ld.getInputStream(); OutputStream fOut = new BufferedOutputStream(new FileOutputStream(outFileName));
Streams.pipeAll(unc, fOut);
fOut.close(); break; } else if (message instanceof PGPOnePassSignatureList) { System.out.println("encrypted message contains a signed message - not literal data."); } else if (message instanceof PGPSignatureList) { System.out.println("encrypted message contains a signed message - not literal data."); } else { throw new PGPException("message is not a simple encrypted file - type unknown."); } message = plainFact.nextObject(); } if (pbe.isIntegrityProtected()) { if (!pbe.verify()) { System.err.println("message failed integrity check"); } else { System.err.println("message integrity check passed"); } } else { System.err.println("no message integrity check"); } } catch (PGPException e) { System.err.println(e); if (e.getUnderlyingException() != null) { e.getUnderlyingException().printStackTrace(); } } }
private static void encryptFile( String outputFileName, String inputFileName, String encKeyFileName, boolean armor, boolean withIntegrityCheck) throws IOException, NoSuchProviderException, PGPException { OutputStream out = new BufferedOutputStream(new FileOutputStream(outputFileName)); PGPPublicKey encKey = PGPExampleUtil.readPublicKey(encKeyFileName); encryptFile(out, inputFileName, encKey, armor, withIntegrityCheck); out.close(); }
private static void encryptFile( OutputStream out, String fileName, PGPPublicKey encKey, boolean armor, boolean withIntegrityCheck) throws IOException, NoSuchProviderException { if (armor) { out = new ArmoredOutputStream(out); }
try { byte[] bytes = PGPExampleUtil.compressFile(fileName, CompressionAlgorithmTags.ZIP);
PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator( new JcePGPDataEncryptorBuilder(PGPEncryptedData.CAST5).setWithIntegrityPacket(withIntegrityCheck).setSecureRandom(new SecureRandom()).setProvider("BC"));
encGen.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(encKey).setProvider("BC"));
OutputStream cOut = encGen.open(out, bytes.length);
cOut.write(bytes); cOut.close();
if (armor) { out.close(); } } catch (PGPException e) { System.err.println(e); if (e.getUnderlyingException() != null) { e.getUnderlyingException().printStackTrace(); } } }
public static void main( String[] args) throws Exception { Security.addProvider(new BouncyCastleProvider());
if (args.length == 0) { System.err.println("usage: KeyBasedFileProcessor -e|-d [-a|ai] file [secretKeyFile passPhrase|pubKeyFile]"); return; }
if (args[0].equals("-e")) { if (args[1].equals("-a") || args[1].equals("-ai") || args[1].equals("-ia")) { encryptFile(args[2] + ".asc", args[2], args[3], true, (args[1].indexOf('i') > 0)); } else if (args[1].equals("-i")) { encryptFile(args[2] + ".bpg", args[2], args[3], false, true); } else { encryptFile(args[1] + ".bpg", args[1], args[2], false, false); } } else if (args[0].equals("-d")) { decryptFile(args[1], args[2], args[3].toCharArray(), new File(args[1]).getName() + ".out"); } else { System.err.println("usage: KeyBasedFileProcessor -d|-e [-a|ai] file [secretKeyFile passPhrase|pubKeyFile]"); } } } asdf
Netty作为一个异步非阻塞式的框架,是不允许在ChannelHandler中长时间处理事务(比如数据库的操作),阻塞I/O的读写处理的。 在Netty in Action中是这样描述的:
While the I/O thread must not be blocked at all, thus prohibiting any direct blocking operations within your ChannelHandler, there is a way to implement this requirement. You can specify an EventExecutorGroup when adding ChannelHandlers to the ChannelPipeline. This EventExecutorGroup will then be used to obtain an EventExecutor, which will execute all the methods of the ChannelHandler. This EventExecutor will use a different thread from the I/O thread, thus freeing up the EventLoop. I/O线程是不允许被阻塞的,也就是不能在ChannelHandler中进行任何阻塞式的处理,但是对此我们也有相应的解决方法. 就是在把ChannelHanders添加到ChannelPipeline的时候,指定一个EventExecutorGroup,ChannelHandler中所有的方法都将会在这个指定的EventExecutorGroup中运行。 而这个EVentExecutorGroup运行的线程与I/O线程不同,达到不阻塞I/O的目的。 程序示例如下: Channel ch = ...; ChannelPipeline p = ch.pipeline(); EventExecutor e1 = new DefaultEventExecutorGroup(16); EventExecutor e2 = new DefaultEventExecutorGroup(8); p.addLast(new MyProtocolCodec()); p.addLast(e1, new MyDatabaseAccessingHandler()); p.addLast(e2, new MyHardDiskAccessingHandler()); 需要补充说明一下,上面的示例程序似乎有点问题。使用上述方法添加ChannelHandler到pipeline中以后,channelHandler的所有方法确实什么在一个单独的线程中被处理。 但是,每次DefaultEventExcutorGroup线程池中的线程不能被重用,每次都会生成一个新的线程,然后在新的线程中调用ChannelHandler, 在visualvm可以看到线程数量直线增长。 解决的方法是:不能使用局部变量形式的DefaultEventExecutorGroup。而使用类静态成员变量: static final EventExecutor e1 = new DefaultEventExecutorGroup(16); 我分析原因可能是:在新的连接到来,创建ChannelPipeline给新Channel的时候,如果不使用静态的共享变量,而使用局部变量的话,就造成DefaultEventExecutorGroup被多次重复创建。因此,虽然一个DefaultEventExecutorGroup中的Thread数量是固定的,但是却产生了多余的DefaultEventExecutorGroup。从VisualVM中也可以看到,DefaultEventExecutorGroup线程的名字会是: xxx-2-1 xxx-3-1 xxx-4-1 xxx-n-1 说明是Group的数量(第一个数字)在增多,而不是Group中的线程数量(第二个数字)在增多 改成静态变量后,线程名会是: xxx-2-1 xxx-2-2 xxx-2-3 xxx-2-n 最后一个n就是在创建DefaultEventExecutorGroup时候,传入的线程个数参数的大小。
Netty 1. there're 2 EventLoopGroup in netty, bossGroup and workerGroup, (1 implementation NioEventLoopGroup is a kind of thread pool) 2. bossGroup is Acceptor,is responsible for creating Channels for incoming connection requests 3. workerGroup is the Reactor/Selector?, handling I/O requests. 4. a thread in bossGroup will be listening in the port, Once a connection has been accepted workerGroup assigns an EventLoop to its Channel 5. multiple channels can be registered into 1 EventLoop, multiple EventLoops will exist in workerGroup 6. workerGroup will iterate all the EventLoop, and iterate all the channels in EventLoop, if any of the channel is ready to execute/process 7. it will invoke all the channelHandlers in the channelPipeline 8. ChannelPipelines are containers for chains of ChannelHandlers which executed in order 9. There are, in fact, two ways of sending messages in Netty. You can write directly to the Channel or write to the ChannelHandlerContext object. The main difference is that the former approach causes the message to start from the tail of the ChannelPipeline, while the latter causes the message to start from the next handler in the ChannelPipeline. 10. While the I/O thread must not be blocked at all, thus prohibiting any direct blocking operations within your ChannelHandler, there is a way to implement this requirement. You can specify an EventExecutorGroup when adding ChannelHandlers to the ChannelPipeline. This EventExecutorGroup will then be used to obtain an EventExecutor, which will execute all the methods of the ChannelHandler. This EventExecutor will use a different thread from the I/O thread, thus freeing up the EventLoop. Channel ch = ...; ChannelPipeline p = ch.pipeline(); EventExecutor e1 = new DefaultEventExecutor(16); EventExecutor e2 = new DefaultEventExecutor(8); p.addLast(new MyProtocolCodec()); p.addLast(e1, new MyDatabaseAccessingHandler()); p.addLast(e2, new MyHardDiskAccessingHandler()); http://stackoverflow.com/questions/12928723/netty-4-eventloopgroup-eventloop-eventexecutor-thread-affinity
http://denis.doublebuffer.net/lablog/2012/11/19/fixing-the-screen-flickering-on-a-dell-inspiron-n5720-and-maybe-many-others/ A Dell Inspiron N5720 has a nVidia GT 630M and an Intel HD Graphics 4000. Let’s see what the Intel Graphics Control panel looks like. To open it, follow these instructions. Select Advanced mode. Go to the Power menu and change the power source to “On battery”. Now uncheck the little check box that says “Display Refresh Rate Switch”. Apply. OK. And you’re done.
Where are the Database Files Stored?When using database URLs like jdbc:h2:~/test , the database is stored in the user directory. For Windows, this is usually C:\Documents and Settings\<userName> or C:\Users\<userName> . If the base directory is not set (as in jdbc:h2:test ), the database files are stored in the directory where the application is started (the current working directory). When using the H2 Console application from the start menu, this is<Installation Directory>/bin . The base directory can be set in the database URL. A fixed or relative path can be used. When using the URL jdbc:h2:file:data/sample , the database is stored in the directory data (relative to the current working directory). The directory is created automatically if it does not yet exist. It is also possible to use the fully qualified directory name (and for Windows, drive name). Example:jdbc:h2:file:C:/data/tes
在主动模式下,FTP客户端随机开启一个大于1024的端口N向服务器的21号端口发起连接,然后开放N+1号端口进行监听,并向服务器发出PORT N+1命令。服务器接收到命令后,会用其本地的FTP数据端口(通常是20)来连接客户端指定的端口N+1,进行数据传输。 在被动模式下,FTP库户端随机开启一个大于1024的端口N向服务器的21号端口发起连接,同时会开启N+1号端口。然后向服务器发送PASV命令,通知服务器自己处于被动模式。服务器收到命令后,会开放一个大于1024的端口P进行监听,然后用PORT P命令通知客户端,自己的数据端口是P。客户端收到命令后,会通过N+1号端口连接服务器的端口P,然后在两个端口之间进行数据传输。 总的来说,主动模式的FTP是指服务器主动连接客户端的数据端口,被动模式的FTP是指服务器被动地等待客户端连接自己的数据端口。
被动模式的FTP通常用在处于防火墙之后的FTP客户访问外界FTp服务器的情况,因为在这种情况下,防火墙通常配置为不允许外界访问防火墙之后主机,而只允许由防火墙之后的主机发起的连接请求通过。 因此,在这种情况下不能使用主动模式的FTP传输,而被动模式的FTP可以良好的工作。
Standard模式FTP 客户端首先和FTP Server的TCP 21端口建立连接,通过这个通道发送命令,客户端需要接收数据的时候在这个通道上发送PORT命令。 PORT命令包含了客户端用什么端口接收数据。 在传送数据的时候,服务器端通过自己的TCP 20端口发送数据。 FTP server必须和客户端建立一个新的连接用来传送数据。 Passive模式在建立控制通道的时候和Standard模式类似,当客户端通过这个通道发送PASV 命令的时候,FTP server打开一个位于1024和5000之间的随机端口并且通知客户端在这个端口上传送数据的请求, 然后FTP server 将通过这个端口进行数据的传送,这个时候FTP server不再需要建立一个新的和客户端之间的连接。
java.awt.Desktop is the class you're looking for.
import java.awt.Desktop; import java.net.URI; // ... if(Desktop.isDesktopSupported()) { Desktop.getDesktop().browse(new URI("http://www.example.com")); }
If you're using Java 6 or above, see the Desktop API, in particular browse. Use it like this (not tested):
// using this in real life, you'd probably want to check that the desktop
// methods are supported using isDesktopSupported()...
String htmlFilePath = "path/to/html/file.html"; // path to your new file
File htmlFile = new File(htmlFilePath);
// open the default web browser for the HTML page
Desktop.getDesktop().browse(htmlFile.toURI());
// if a web browser is the default HTML handler, this might work too
Desktop.getDesktop().open(htmlFile);
例子: java -cp bin;lib/* org.kevin.task.RunJobA f:\temp\jobs 1.路径中需要使用/,而不是\\, 2.只能用一个*,不需要添加后缀,
P66 1. components: a. Message a.1 Header a.1.1 Destination a.1.2 DeliveryMode: persistent, non-persistent a.2 Body b. Queue c. Client c.1 Producer c.2 Consumer - Message Selector d. Domain, 2 styles of messaging d.1 point-to-point d.1.1 destination known as QUEUE d.1.2 similar to person to person email sent through a mail server d.1.3 multiple consumers can be registered on a single queue but only one consumer will receive a a given messge and it's upt to that consumer to acknowledge the message d.2 publish-subscribe d.2.1 destination known as TOPICS d.2.2 publishers send messages to the topic and subscribers register to receive messages from the topic d.2.3 durable subscriptions allow for subscriber disconnection without missing any messages
最近在开发中遇到一个问题,就是如何判断远端服务器是否已经断开连接,如果断开那么需要重新连接。 首先想到socket类的方法isClosed()、isConnected()、isInputStreamShutdown()、isOutputStreamShutdown()等,但经过试验并查看相关文档,这些方法都是本地端的状态,无法判断远端是否已经断开连接。 然后想到是否可以通过OutputStream发送一段测试数据,如果发送失败就表示远端已经断开连接,类似ping,但是这样会影响到正常的输出数据,远端无法把正常数据和测试数据分开。 最后又回到socket类,发现有一个方法sendUrgentData,查看文档后得知它会往输出流发送一个字节的数据,只要对方Socket的SO_OOBINLINE属性没有打开,就会自动舍弃这个字节,而SO_OOBINLINE属性默认情况下就是关闭的,太好了,正是我需要的! 于是,下面一段代码就可以判断远端是否断开了连接: Java代码 ![收藏代码](http://feikiss.iteye.com/images/icon_star.png) - try{
- socket.sendUrgentData(0xFF);
- }catch(Exception ex){
- reconnect();
- }
有一个WEB应用,使用了AJAX框架,所有页面的展示都是从AJAX的函数中进行的。 在这种情况,有一个很简单的方法来下载一个文件: window.location="download.action?para1=value1...."
run in the mysql command line client next sequence of queries:
USE mysq; SHOW TABLE STATUS; SHOW INDEX FROM `columns_priv`; DESCRIBE `columns_priv`; SHOW INDEX FROM `db`; DESCRIBE `db`;
describe `column_priv`; ERROR `(HY000): Can't create/write to file 'c:\windows\Temp\#sql_7a4_0.MYD (Errcode: 2)
Delete the file #sql_7a4_0.MYD if exists
在apache dbutil中,给数据列起别名有时候不起作用。这时候,需要在连接字符串中添加一个参数:
useOldAliasMetadataBehavior=true";
Mysql replication error 1201 On a fine happy morning I am greeted with an alert that slave is not running. Running start slave yields this: ERROR 1201 (HY000): Could not initialize master info structure; more error messages can be found in the MySQL error log Enabling the log yielded nothing. Googling yielded This Page that helped me a lot, but I didn't have to do quite as much work all over again. Here is what I did. First, I saved my "SHOW SLAVE STATUS\G" output: ************************** 1. row ************************** Slave_IO_State: Master_Host: 127.0.0.1 Master_User: replication Master_Port: 3307 Connect_Retry: 60 Master_Log_File: mysqld-bin.000401 Read_Master_Log_Pos: 98 Relay_Log_File: mysqld-relay-bin.006135 Relay_Log_Pos: 242 Relay_Master_Log_File: mysqld-bin.000401 Slave_IO_Running: No Slave_SQL_Running: No Replicate_Do_DB: Replicate_Ignore_DB: Replicate_Do_Table: Replicate_Ignore_Table: Replicate_Wild_Do_Table: Replicate_Wild_Ignore_Table: Last_Errno: 0 Last_Error: Skip_Counter: 0 Exec_Master_Log_Pos: 98 Relay_Log_Space: 0 Until_Condition: None Until_Log_File: Until_Log_Pos: 0 Master_SSL_Allowed: No Master_SSL_CA_File: Master_SSL_CA_Path: Master_SSL_Cert: Master_SSL_Cipher: Master_SSL_Key: Seconds_Behind_Master: NULL 1 row in set (0.00 sec) STOP SLAVE; RESET SLAVE; mysql> CHANGE MASTER TO MASTER_LOG_POS=98 , MASTER_LOG_FILE = 'mysqld-bin.000401'; Query OK, 0 rows affected (0.04 sec) mysql> start slave; Query OK, 0 rows affected (0.00 sec) The position comes from the output I saved before It's working fine again. Still don't know what caused this - possibly random remote server power cycling...
picky挑剔的; quick-tempered容易发脾气的; rude粗鲁无礼的; scatter-brained 记性不好的; slapdash粗心大意的; sly狡猾的; spiteful怀恨在心的; thoughtless草率的; fussy挑剔的; manic狂躁的; manipulative喜欢指使别人的; moody情绪化的
package org.springside.examples.miniweb.dao.account; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import org.springframework.stereotype.Component; import org.springside.examples.miniweb.entity.account.Group; import org.springside.examples.miniweb.entity.account.User; /** * GroupDao的扩展行为实现类. */ @Component public class GroupDaoImpl implements GroupDaoCustom { private static final String QUERY_USER_BY_GROUPID = "select u from User u left join u.groupList g where g.id=?"; @PersistenceContext private EntityManager em; @Override public void deleteWithReference(Long id) { //因為Group中沒有与User的关联,只能用笨办法,查询出拥有该权限组的用户, 并删除该用户的权限组. Group group = em.find(Group.class, id); List<User> users = em.createQuery(QUERY_USER_BY_GROUPID).setParameter(1, id).getResultList(); for (User u : users) { u.getGroupList().remove(group); } em.remove(group); } }
造成这个错误的原因主要是一些系统文件夹system权限丢失。 所以只要把权限加回去就行了。 我主要查看了 c:\windows\installer C:\Documents and Settings\<user name>\Local Settings\Temp 右键点击目录,点安全选项页,在“组和用户名称”列表中把system用户添加进去,并在下面的权限列表中勾选“完全控制” 即可
Spring Security中指定session超时后跳转的页面
在xml配置文件中加入:<session-management invalid-session-url="/session-timeout.htm" />
在applicationContext.xml中定义了一个DataSource:<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" /> 但是在代码中,使用anotation进行注入的时候,却总是找不到这个dataSource. @Autowired public void setDataSource(DataSource dataSource) { jdbcTemplate = new JdbcTemplate(dataSource); jdbcInsert = new SimpleJdbcInsert(dataSource); } 最后终于想明白了,原因大概是这样的,使用autowired的时候,默认是根据类型来匹配的,在xml中定义的类型是:BasicDataSource,而不是接口DataSource,所以默认情况下这样是无法自动装配的。解决办法是指令使用名字来进行bean的匹配,也就是用Qualifier指定bean的id. @Autowired public void setDataSource(@Qualifier("dataSource") DataSource dataSource) { jdbcTemplate = new JdbcTemplate(dataSource); jdbcInsert = new SimpleJdbcInsert(dataSource); } 另外一点,在网上搜索的过程中发现有不少人都有类似的问题,但是他们的原因是没有正确使用spring的注入,而是自己在代码中new了一个Dao的实例,这样的话,spring是无法将dataSource注入到dao的实例中的
HeidiSql是一家德国公司研发的轻量级的,开源mysql客户端工具。体积十分小巧,可是十分实用。
我之所以喜欢的原因: 1,有导入/导出的功能,可以将数据直接从文本文件中导入到数据库的数据表中。 2,可以将选中的数据导出成为sql语句 3,界面布置十分合理,操作简便
在FreeMarker中使用:${rc.getMessage("key.in.properties")}
mysql数据复制的一个非常有用的选项。
大部分情况下,我使用两个mysql数据库双向复制。 结构大致如下: A <==> B 但是,现在有了新需要,要求在另一个机器上复制出生产环境的所有数据,但是这些数据的复制是单向,也就是说数据只会从生产环境复制到新的服务器上,而所有新数据库的数据变化不会复制到生产环境中。
新的结构大致如下: A <==> B --> C (新的服务器) 设置数据单向复制的步骤其实非常简单,只要在新的服务器(也就是slave端)指定master的 1,在服务器端B,先stop slave; 2, show master status, 记录文件名和position 3,backup the whole database 4,start slave 5, 在slave端,restore database 6, change master .... 到目前为止,看上去一切都进展顺利,但是马上我就发现slave端只有服务器B的数据。因为服务器B没有把从服务器A中复制过来的数据记录到日志中。 所以,slave C就无法得到此部分数据。 后来,经过网上搜索发现有一个选项 -log_slave_update,可以让服务器B记录此部分数据。 实践中,在服务器B中加入此选项后,机器C中就能够获得所有的数据。
主要是使用: Biff8EncryptionKey.setCurrentUserPassword(password); 在打开workbook之前 HSSFWorkbook workbook = new HSSFWorkbook(inp); 注意事项:这个应该是只适用于xls,而不是xlsx
Tapestry5 in action现在还只是提供MEAP形式的订购,MEAP-Manning Early Access Program, 也就是提前试阅的意思,如果读者订购了此版本,那么manning会在作者每完成一章后,将该章节发给订购者,订购者可以提交自己的反馈,manning会根据情况发布相应的错误修改信息。
目前此书的第一章可以免费下载:
http://www.blogjava.net/Files/usherlight/Tapestry5%20in%20action%20MEAP%20ch01.zip
我使用的是sun提供的javax.comm包,不确定其他的comm支持包(比如:rxtx)行为与之相同
1、事件驱动的数据读取
需要注意的是:一个SerialPort只能注册一个事件监听程序,因此,有时候有会发现,你的事件监听处理程序会变得非常庞大
另外,要注意的就是事件处理如果比较耗时的话,最好是在单独的线程中运行,否则会阻塞数据的接收。
串口接收到数据后,默认在线程: Win32SerialPort Notification thread中运行。如果不及时释放的话,会造成数据无法读取。
我的做法是,接收到数据后,先判断数据的合法和完整性,如果没有接收到完整的数据,则在缓存数据后,马上返回
如果数据完整而且合法,则另开一个线程,进行数据处理
2、数据的发送
建议单独使用一个线程来发送数据,目的还是为了防止阻塞,有一个技巧就是使用OUTPUT_BUFFER_EMPTY事件来发送。
uTorrent
7-zip
CDBurnerXP
Notepad++
OpenOffice(LibreOffice)
FireFox
VirtualBox(vmware player)
WinCDEmu
搜狗五笔
打开Sun virtualBox界面,点击设置,点“数据空间”,再点右边带有加号的图标,这时选择你想共享的文件夹,有只读模式与固定模式,选择一种,只读模式是指在虚拟系统中你不能操作这个文件夹,只能读文件,固定模式是指完全操作。
然后打开你的虚拟系统,点开界面上的菜单“设备”选择最后一项“安装增强功能”虚拟系统开始安装,结束后重启虚拟系统。在虚拟系统中打开资源管理器,右击最下面的“网络”,点开“映射网络驱动器”浏览,打击“virtualbox..."(要等会)找到前面共享的了文件夹,确定,
然后你应该在你的资源管理器中就可以看到你刚刚映射的驱动器,你可以设置多个共享文件夹,步骤如前。你想用主机上哪个文件,你就把它复制到共享的文件夹就可以了。
1、用@RequestParam获取HttpServletRequest里的参数值相当方便,spring可以进行自动的类型转换
2、对于Restful的支持,结合@RequestMapping的UriTemplate和method以及@PathVariable,非常方便地实现了restful的url
3、对于Ajax的支持,@RespsonseBody提供了方便的机制
4、灵活丰富的@Controller的函数返回类型
5、可以方便地给@Controller函数注入各种资源
其他的一些别的框架都有特性,诸如国际化,文件上传什么的就不说了
1、Tapestry5.2.4的发布,Tapestry5.2.3版本在内部投票中被否决(主要是因为使用maven快速创建的原型有问题),所以在被否决的3天后便发布了5.2.4,相对5.2.2来说,变动并不大,只有8个错误修复和5个功能改进。但是至少说明Tapestry的项目还在顺利的进行中。
2、Tapestry主页的全新改版。主要是完善了文档。这一点是非常重要的,Tapestry项目组的成员也承认Tapestry在推销自己或者是在市场推广方面做得非常失败(very bad in marketing),所以最近也采取了一些措施来进行改变,比如,预计明年会发行Tapestry5 in action一书等等。
3、还有一件事需要提及的是:appfuse的作者最近对web框架进行了一番对比http://raibledesigns.com/rd/entry/my_comparing_jvm_web_frameworks,Tapestry在13个框架中名列第7,刚好是中间的位置。Tapestry项目的成员颇有不满,认为作者Matt Raible对Tapestry不够了解,有误导观众之嫌。Tapestry项目成员Igor E. Poteryaev认为Matt在 认识度,开发效率,项目健康度,测试友好性,scalability等方面严重低估了Tapestry的能力。
1、发布了MyBatis3.0.3, 具体内容不详,没有能够发现release notes.
2、发布了mybatis-generator 1.3.0, 这是一个能够根据数据库的表自动生成mybatis的
sqlmap xml文件
与数据表对应的java class
使用上两个文件的java类
我下载后,尝试着使用了一下,发现需要有一个配置文件,我觉得应该是用于提供数据库连接的信息。但是配置文件的格式不详。
现在发现mybatis的文档比较成问题,很多东西都没有说明,使用起来很困难啊。
3、新增了两个中方翻译文档
# MyBatis 3 User Guide Simplified Chinese.pdf
# MyBatis-Spring Reference Simplied Chinese.pdf
4、发布了mybatis-spring-1.0.0-RC2
5、发布了mybatis-guice-1.0.0-RC3
主要是一些bug修复和改进,最重要的一点是能够从link对象生成绝对路径了。
这主要是便于ajax的使用。
Bug
* Element.forceAttribute uses the element's namespace to match the attribute.
* Element.attribute(String name, String value) adds elements that already exist
* Element#addClassName can create an additional new 'class' attribute
* Properties defined in an Interface are not exposed by PropertyAccess for abstract classes that do not directly implement the methods
* Some services require a notification that they have been reloaded, so they can clean up external dependencies
* Whitespaces in SymbolConstants.SUPPORTED_LOCALES cause that locales are not persised
* Validation macros do not work when used in @Validate annotation
* Client-side validation of @Pattern is broken
* Linking a Form to a Zone will no longer work unless the Form contains validated fields
* When using PropertyShadowBuilder to build a service, if the property is null, an immediate exception is needed (rather than a NullPointerException)
* When using a MultiZoneUpdate, Tapestry will clear the referenced zone
Improvement
* Add the facility to optionally disable on-focus-change triggered validation while retaining the on-form-submit validation
* Form component should be able to render a secure URL even on an insecure page
* New annotations @Decorate and @Advise to identify methods that decorate or annotate services
* Extend Link with new methods for producing absolute URLs (that include scheme, hostname, etc.)
* Simplify connecting a link or form to an (enclosing) Zone
* BeanBlockContribution should be split into two sub-classes: EditBlockContribution and DisplayBlockContribution
* Define a special CSS class to prevent a client-side form from submitting normally (for Ajax use cases)
* Additional method for Link: addParameterValue(String,Object) that uses ContextPathEncoder to encode object value to a string
* SeleniumTestCase should expose the underlying CommandProcessor, to allow execution of commands not defined by the Selenium interface
* Allow individual SeleniumTestCases to run w/o configuring SeleniumLauncher
今天在网上转了转,发现Tapestry又有了新动作。
1、从5.2.0alpha版8月11日发布到现在,终于发布5.2.1Beta版了。这个版本主要是修改alpha版本中的bug.
5.2相对于5.1进行了许多重大的改进,详细内容可见:Tapestry5.2的新变化
2、据称在5.3版中将引入大量的新功能,具体内容不详。
3、以往最令人诟病的文档问题,目前开发小组也在努力解决。
首先,建立了新的文档网页:http://people.apache.org/~uli/tapestry-site/,不过目前这个网站还没有完全完成,只能算是预览版,但是这毕竟是在正确的道路上前进。
其次,Tapestry 5 in action一书正在写作中,已经完成20%,预计明年一季度能够出版。
我相信,随着功能的不断完善和补充,再加上文档的逐渐充实,Tapestry完全有理由能吸引更多的开发者来关注和使用。
这是mybatis从ibatis更名过来后,发布的第2个版本(第1个版本是3.0.1)。
在这个版本中只修复了4个bug(感觉数目有点少,难道是因为上一个版本的bug真的如此之少?),增加了一个小功能.
作者的话:尽管只有5个修改,但我还是觉得值得为此发布一个新版本。
这5个修改是:
1、在org.apache.ibatis.Session中增加了一个新方法:void select(String statement, ResultHandler handler);
原来只有:
void select(String statement, Object parameter, ResultHandler handler);
void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler);
2、修复了ManagedConnection中关闭连接的问题
3、修复了schema migration中,语句提交(statement commit)不正确的问题
4、修复了延迟加载已经预读取属性时的问题
5、修复了schema migration中,FileInputStream没有正确关闭的问题
介绍一个使用GIMP来批量处理图像的方法。 GIMP是一个免费的图像编辑工具,功能非常强大。并且提供了类似于firefox的插件机制,可以通过添加插件来不断地获得新功能。
批量处理图像就是通过一个批处理插件来完成的。这个插件的下载地址:http://members.ozemail.com.au/~hodsond/dbp.html
插件下载后,解压缩,然后把dbp.exe放在plugin目录下,比如:C:\Program Files\GIMP-2.0\lib\gimp\2.0\plug-ins
启动GIMP,在菜单 Filter 里会发现一个新的选项:Batch Process...
点击这个选项,会弹出一个新窗口,这个就是批处理的操作界面了。
通过这个插件,我们可以完成 批量改变大小/重命名/旋转/锐化/模糊/调整亮度/裁剪 这些功能。
Tapestry开发小组中的一个成员的blog中记录了5.2版本带来的新变化
1、增强了class reload的功能(我个人也认为这一点是5.2版本最令人激动的改进),以前Tapestry和其他的web框架类似,修改页面不需要重新启动application server, 但是修改了类之后,必须重新启动应用服务器才行。但是在Tapestry5.2中,对这一点进行了修改,如果只改变了接口的实现类,而不改变接口的方法签名,就不需要重新启动应用服务器
2、新增了若干个组件,比如:Error和Trigger。
Error和原有的Errors类似,但是Error用于给指定的组件显示验证错误信息。
Trigger提供了在泻染页面过程中触发任务事件的功能,常常用于通过RenderSupport来给页面添加JavaScript代码
3、新的插件(Mixin),包括RenderClientId, RenderNotification
4、集成了JSR-303 Bean的验证,现在可以在页面中使用JSR-303标准的注解来给字段指定需要的验证
5、新的注解,包括@Contribute,@RequestParameter, @ActivationRequestParameter, 使用后两个注解能很容易地获取request中的参数
6、新的页面生命周期事件:pageReset
7、链接修饰过程中的新事件: decoratePageRenderLink, decoreateComponentEventLink
8、页面解析器的更换,原来使用StAX,造成了对Google App Engine和对OSGI的不兼容,5.2版本中使用了标准的SAX解析器
9、页面缓冲池的废除(我认为这是5.2版本的一个相当大而且也是非常重要的一个变化,我认为页面缓冲池技术是tapestry学习曲线陡峭的一个重要原因),5.2版本中所有页面将只有一个实例(也就是lewis howard说的单例化),页面属性的值将会在每个线程中使用一个Map来保存。这样一个页面实例可以在多个线程中使用,而不会有同步问题。
但是,由于这是一个新的尝试,所以lewis也不确定这样做的效果是否很好(详见:http://tapestryjava.blogspot.com/2010/07/everyone-out-of-pool-tapestry-goes.html)所以,在5.2中可以通过配置恢复页面缓冲池的使用。
jquery有一个很方便的插件UI Table Filter可以根据输入的内容隐藏显示表格中相应的数据行。
因为目前使用的tapestry捆绑的是prototype,所以就自己写了一个类似的插件。
<html>
<head>
<script src="prototype-1.6.0.2.js" type="text/javascript"></script>
<script language="javaScript">
Event.observe(window, 'load', function() {
Event.observe('filter', 'keyup', filterTable);
});
function filterTable() {
var filterCaseElement = document.getElementById('filterCase');
var caseSensitive = filterCaseElement.checked;
$$('tr').each(function(trElement, ind) {
var val = $('filter').value;
if ( ! caseSensitive ) {
val = val.toLowerCase();
}
trElement.childElements().each(function(tdElememt) {
var tdText = tdElement.innerText;
if ( ! caseSensitive ) {
tdText = tdText.toLowerCase();
}
if (tdText.include(val)) {
trElement.show();
} else if ( ind > 0 ) {
trElement.hide();
}
});
});
}
</script>
</head>
<body>
filter: <input type="text" id="filter" name="filter"/> <input type="checkbox" id="filterCase" onchange="javaScript:filterTable();"/> Case-Sensitive
<table border="1">
<thead>
<tr>
<th>name</th>
<th>column1</th>
<th>column2</th>
<th>column3</th>
<th>column4</th>
<th>column5</th>
<th>column6</th>
</tr>
</thead>
<tr>
<td>TEST</td>
<td>00150002331</td>
<td>238156</td>
<td>075</td>
<td>001</td>
<td>172.16.14.20</td>
<td>1-1-05</td>
</tr>
<tr>
<td>TEST-2</td>
<td>00150002332</td>
<td>238157</td>
<td>075</td>
<td>002</td>
<td>172.16.14.21</td>
<td>1-1-05</td>
</tr>
<tr>
<td>TEST</td>
<td>00150002333</td>
<td>238158</td>
<td>075</td>
<td>003</td>
<td>172.16.14.23</td>
<td>1-1-05</td>
</tr>
<tr>
<td>TEST</td>
<td>00150002341</td>
<td>238159</td>
<td>075</td>
<td>004</td>
<td>172.16.14.24</td>
<td>1-1-05</td>
</tr>
<tr>
<td>TEST</td>
<td>00150002339</td>
<td>238186</td>
<td>075</td>
<td>006</td>
<td>172.16.14.26</td>
<td>1-1-06</td>
</tr>
</table>
</body>
</html>
如果页面中多个表格,而只需要对其中的一个表格的数据进行过滤的话,简单地把其中:$$('tr').each(function(ele, index) 改成 $$('#tableId, tr').each(function(ele, index) 就行了,其中的tableId就是表格的id
今天打开tapestry的网站,发现tapestry5.2的alpha版本已经发布出来了。
在5.2中还是新增了不少令人激动的功能
1、QueryParameterMapped注解,使用这个注解可以很方便地把request中的参数映射到bean里的属性
2、(这是最让我期待的功能)服务终于能够动态加载了!
3、在事件处理方法中可以使用QueryParameter来注解参数
4、submit组件现在可以cancel表单里的数据了
5、不再使用StAX作为tml的解析器了,而是采用标准的SAX解析器,这样减少外界引用,而且兼容性更好
6、组件中也可以使用SessionAttribute来获得session中的数据了,以前好像只有在page中才能用
7、引入了JSR-303 Bean Validation Integration Library.
GIMP(http://www.gimp.org/)是一个开源的图形处理工具,功能与PHOTOSHOP相比也并不相差太多。
1、选择要保留的图形,
有2种办法,第一种就是直接用魔术棒工具来选择,但一般来说这部分图形的色彩比较复杂,选择起来相对困难
第2种办法是先选择背景,然后使用“反向选择”。
友情提示:在使用魔术棒的时候,有4种模式,可以利用其中的添加,减少来不断地修正选择的范围
2、打开图层面板
3、菜单:选择->浮动,在图层面板中可以看到多一个浮动的图层,但是现在图层里还没有东西
4、菜单:图层->创建,这时候就可以看到,在刚才多出来的浮动图层中出现了选择好的要保留的图像
5、去除背景图层,点击背景图层前面的眼睛,使得背景图层不可见
保存,生成透明背景的新图像
CardLayout布局管理器能够帮助用户处理两个以至更多的成员共享同一显示空间,它把容器分成许多层,每层的显示空间占据整个容器的大小,但是每层只允许放置一个组件,当然每层都可以利用Panel来实现复杂的用户界面.布局管理器(CardLayout)就象一副叠得整整齐齐的扑克牌一样,有54 张牌,但是你只能看见最上面的一张牌,每一张牌就相当于布局管理器中的每一层.
流式布局管理器把容器看成一个行集,好象平时在一张纸上写字一样,一行写满就换下一行。行高是用一行中的控件高度决定的。FlowLayout是所有 JApplet/JApplet的默认布局。在生成流式布局时能够指定显示的对齐方式,默认情况下是居中(FlowLayout.CENTER)
GridLayout 将成员按网格型排列,每个成员尽可能地占据网格的空间,每个网格也同样尽可能地占据空间,从而各个成员按一定的大小比例放置。如果你改变大小, GridLayout将相应地改变每个网格的大小,以使各个网格尽可能地大,占据Container容器全部的空间。
基本布局策略是把容器的空间划分成若干行乘若干列的网格区域,组件就位于这些划分出来的小区域中,所有的区域大小一样。组件按从左到右,从上到下的方法加入。
BoxLayout布局能够允许将控件按照X轴(从左到右)或者Y轴(从上到下)方向来摆放,而且沿着主轴能够设置不同尺寸。
HTTP Status Codes Explained
All valid HTTP 1.1 Status Codes simply explained.
HTTP, Hypertext Transfer Protocol, is the method by which clients (i.e. you) and servers communicate. When someone clicks a link, types in a URL or submits out a form, their browser sends a request to a server for information. It might be asking for a page, or sending data, but either way, that is called an HTTP Request. When a server receives that request, it sends back an HTTP Response, with information for the client. Usually, this is invisible, though I'm sure you've seen one of the very common Response codes - 404, indicating a page was not found. There are a fair few more status codes sent by servers, and the following is a list of the current ones in HTTP 1.1, along with an explanation of their meanings.
A more technical breakdown of HTTP 1.1 status codes and their meanings is available at http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html. There are several versions of HTTP, but currently HTTP 1.1 is the most widely used.
Informational
- 100 - Continue
A status code of 100 indicates that (usually the first) part of a request has been received without any problems, and that the rest of the request should now be sent.
- 101 - Switching Protocols
HTTP 1.1 is just one type of protocol for transferring data on the web, and a status code of 101 indicates that the server is changing to the protocol it defines in the "Upgrade" header it returns to the client. For example, when requesting a page, a browser might receive a statis code of 101, followed by an "Upgrade" header showing that the server is changing to a different version of HTTP.
Successful
- 200 - OK
The 200 status code is by far the most common returned. It means, simply, that the request was received and understood and is being processed.
- 201 - Created
A 201 status code indicates that a request was successful and as a result, a resource has been created (for example a new page).
- 202 - Accepted
The status code 202 indicates that server has received and understood the request, and that it has been accepted for processing, although it may not be processed immediately.
- 203 - Non-Authoritative Information
A 203 status code means that the request was received and understood, and that information sent back about the response is from a third party, rather than the original server. This is virtually identical in meaning to a 200 status code.
- 204 - No Content
The 204 status code means that the request was received and understood, but that there is no need to send any data back.
- 205 - Reset Content
The 205 status code is a request from the server to the client to reset the document from which the original request was sent. For example, if a user fills out a form, and submits it, a status code of 205 means the server is asking the browser to clear the form.
- 206 - Partial Content
A status code of 206 is a response to a request for part of a document. This is used by advanced caching tools, when a user agent requests only a small part of a page, and just that section is returned.
Redirection
- 300 - Multiple Choices
The 300 status code indicates that a resource has moved. The response will also include a list of locations from which the user agent can select the most appropriate.
- 301 - Moved Permanently
A status code of 301 tells a client that the resource they asked for has permanently moved to a new location. The response should also include this location. It tells the client to use the new URL the next time it wants to fetch the same resource.
- 302 - Found
A status code of 302 tells a client that the resource they asked for has temporarily moved to a new location. The response should also include this location. It tells the client that it should carry on using the same URL to access this resource.
- 303 - See Other
A 303 status code indicates that the response to the request can be found at the specified URL, and should be retrieved from there. It does not mean that something has moved - it is simply specifying the address at which the response to the request can be found.
- 304 - Not Modified
The 304 status code is sent in response to a request (for a document) that asked for the document only if it was newer than the one the client already had. Normally, when a document is cached, the date it was cached is stored. The next time the document is viewed, the client asks the server if the document has changed. If not, the client just reloads the document from the cache.
- 305 - Use Proxy
A 305 status code tells the client that the requested resource has to be reached through a proxy, which will be specified in the response.
- 307 - Temporary Redirect
307 is the status code that is sent when a document is temporarily available at a different URL, which is also returned. There is very little difference between a 302 status code and a 307 status code. 307 was created as another, less ambiguous, version of the 302 status code.
Client Error
- 400 - Bad Request
A status code of 400 indicates that the server did not understand the request due to bad syntax.
- 401 - Unauthorized
A 401 status code indicates that before a resource can be accessed, the client must be authorised by the server.
- 402 - Payment Required
The 402 status code is not currently in use, being listed as "reserved for future use".
- 403 - Forbidden
A 403 status code indicates that the client cannot access the requested resource. That might mean that the wrong username and password were sent in the request, or that the permissions on the server do not allow what was being asked.
- 404 - Not Found
The best known of them all, the 404 status code indicates that the requested resource was not found at the URL given, and the server has no idea how long for.
- 405 - Method Not Allowed
A 405 status code is returned when the client has tried to use a request method that the server does not allow. Request methods that are allowed should be sent with the response (common request methods are POST and GET).
- 406 - Not Acceptable
The 406 status code means that, although the server understood and processed the request, the response is of a form the client cannot understand. A client sends, as part of a request, headers indicating what types of data it can use, and a 406 error is returned when the response is of a type not i that list.
- 407 - Proxy Authentication Required
The 407 status code is very similar to the 401 status code, and means that the client must be authorised by the proxy before the request can proceed.
- 408 - Request Timeout
A 408 status code means that the client did not produce a request quickly enough. A server is set to only wait a certain amount of time for responses from clients, and a 408 status code indicates that time has passed.
- 409 - Conflict
A 409 status code indicates that the server was unable to complete the request, often because a file would need to be editted, created or deleted, and that file cannot be editted, created or deleted.
- 410 - Gone
A 410 status code is the 404's lesser known cousin. It indicates that a resource has permanently gone (a 404 status code gives no indication if a resource has gine permanently or temporarily), and no new address is known for it.
- 411 - Length Required
The 411 status code occurs when a server refuses to process a request because a content length was not specified.
- 412 - Precondition Failed
A 412 status code indicates that one of the conditions the request was made under has failed.
- 413 - Request Entity Too Large
The 413 status code indicates that the request was larger than the server is able to handle, either due to physical constraints or to settings. Usually, this occurs when a file is sent using the POST method from a form, and the file is larger than the maximum size allowed in the server settings.
- 414 - Request-URI Too Long
The 414 status code indicates the the URL requested by the client was longer than it can process.
- 415 - Unsupported Media Type
A 415 status code is returned by a server to indicate that part of the request was in an unsupported format.
- 416 - Requested Range Not Satisfiable
A 416 status code indicates that the server was unable to fulfill the request. This may be, for example, because the client asked for the 800th-900th bytes of a document, but the document was only 200 bytes long.
- 417 - Expectation Failed
The 417 status code means that the server was unable to properly complete the request. One of the headers sent to the server, the "Expect" header, indicated an expectation the server could not meet.
Server Error
- 500 - Internal Server Error
A 500 status code (all too often seen by Perl programmers) indicates that the server encountered something it didn't expect and was unable to complete the request.
- 501 - Not Implemented
The 501 status code indicates that the server does not support all that is needed for the request to be completed.
- 502 - Bad Gateway
A 502 status code indicates that a server, while acting as a proxy, received a response from a server further upstream that it judged invalid.
- 503 - Service Unavailable
A 503 status code is most often seen on extremely busy servers, and it indicates that the server was unable to complete the request due to a server overload.
- 504 - Gateway Timeout
A 504 status code is returned when a server acting as a proxy has waited too long for a response from a server further upstream.
- 505 - HTTP Version Not Supported
A 505 status code is returned when the HTTP version indicated in the request is no supported. The response should indicate which HTTP versions are supported.
摘要: 一、往串口写数据
import java.io.*;
import javax.comm.*;
import java.util.*;
public class PortWriter
{
static Enumeration ports;
... 阅读全文
"java decompiler"是一个非常出色的java反编译工具,详见主页:http://java.decompiler.free.fr/
在myeclipse8.5中的安装也比较简单,下载:http://java.decompiler.free.fr/jd-eclipse/update/jdeclipse_update_site.zip
然后把这个压缩包解开放在myeclipse8.5的dropins目录下,比如:C:\Programs\Genuitec\MyEclipse-8.5\dropins\jdeclipse_update_site
重起myeclipse就行了,第一次打开class文件,可能会稍微有一点慢,要等待一小会才会反编译出来。
好像还有点问题
操作系统升级到windows7之后,原来很多软件就不能用了。对于虚拟光驱软件,我推荐一款免费的:WinCDEmu,这是一个在SourceForge上的免费软件。优点是使用方便,他可以虚拟出任意多个虚拟光驱,而且可以选择手工指定盘符。只要双击文件,就会弹出一个对话框,询问光驱的盘符。当然你也可以选择让WinCDEmu自动选择盘符。他支持大部分主流的文件格式: ISO, CUE, NRG, MDS/MDF, CCD, IMG
Recently I got a chance working with Spring security, formerly known as Acegi Security for spring. While working with the framework, I heard comments from friends and colleagues saying that spring security lacks proper documentation. So thought of sharing a little knowledge. By the way, this is first ever blog posting and kindly excuse me and let me know any errors and improvements.
Spring security offers a simple configuration based security for your web applications helping you secure your web application with out littering your business logic with any security code. It provides securing URL's based on the Role (Authorities), securing your business methods based on the ACL's.
The first step in hooking up the spring security to your web application is by specifying the DelegatingFilterProxy in your web.xml.
springSecurityFilterChain
org.springframework.web.filter.DelegatingFilterProxy
springSecurityFilterChain
/*
REQUEST
INCLUDE
FORWARD
If you want to externalize all of your security related configuration into a separate file, you can do so and add that to your context location param.
contextConfigLocation
/WEB-INF/beans.xml , /WEB-INF/springSecurity.xml
Now comes the part of security configuration for your application, Adding the URL security patterns is pretty simple and straight forward. Add all the URL patterns which you want to secure and add the wild card pattern at the end. You need to have some default principal and role even for non logged in users as you need to give access to pages like log in, register and forgot password kind of functionality even to non logged in users.
I tried to add comments to pretty much every element which I am using here.
As an example I added just a wild card intercept url which make every page of my application secure. You need to exclude different urls based on the roles.
Following is my custom implementation of AuthenticationEntryPoint, which currently is not doing any thing except leveraging the commence to its super class which is the spring implementation of AuthenticationProcessingFilterEntryPoint. I hooked it to add any custom logic.
public class CustomAuthenticationEntryPoint extends AuthenticationProcessingFilterEntryPoint {
private static final Log logger = LogFactory.getLog(CustomAuthenticationEntryPoint.class);
@Override
public void commence(ServletRequest request, ServletResponse response, AuthenticationException authException) throws IOException, ServletException {
super.commence(request, response, authException);
}
}
This is my custom authentication manager which actually does the custom login of the user. It will throw an BadCredentialsException in case of invalid credentials or thorws a AuthenticationServiceException in case of a service error (Database error, SQL error or any other error).
public class CustomAuthunticationManager implements AuthenticationManager {
@Autowired
UserManagerService userManagerService;
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if(StringUtils.isBlank((String) authentication.getPrincipal()) || StringUtils.isBlank((String) authentication.getCredentials())){
throw new BadCredentialsException("Invalid username/password");
}
User user = null;
GrantedAuthority[] grantedAuthorities = null;
try{
user = userManagerService.getUser((String) authentication.getPrincipal(), (String) authentication.getCredentials());
}
catch(InvalidCredentialsException ex){
throw new BadCredentialsException(ex.getMessage());
}
catch(Exception e){
throw new AuthenticationServiceException("Currently we are unable to process your request. Kindly try again later.");
}
if (user != null) {
List roles = user.getAssociatedRoles();
grantedAuthorities = new GrantedAuthority[roles.size()];
for (int i = 0; i < roles.size(); i++) {
Role role = roles.get(i);
GrantedAuthority authority = new GrantedAuthorityImpl(role.getRoleCode());
grantedAuthorities[i] = authority;
}
}
else{
throw new BadCredentialsException("Invalid username/password");
}
return new UsernamePasswordAuthenticationToken(user, authentication.getCredentials(), grantedAuthorities);
}
}
At the client side (jsp), the simple configuration you need to do is post the request to"/j_spring_security_check" with parameters "j_username" and "j_password".
That's pretty much all you need to do for enabling spring security to your existing web application. I will try to explain about doing the method security using ACL's and configuring the view using spring security tags in another post.
作者相信ga版本将会在不久的未来发布。
所作者透露,在新版本中会增加一个新的组件:dynamic.
这个组件的用途是更换皮肤。在旧版本的tapestry中,由于tapestry的模板是一致的,所以要实现换肤相当困难。
但在新版本中的这个组件比较完美地解决了这个问题。
作者自我评价这个组件是令人激动的一个新事物。
现在打开iBatis的主页:ibatis.apache.org会发现作者发布了一个新闻或者说是声明。
大意是因为各种原因,已经把iBatis更名为MyBatis, 并且搬迁到Google Code中了。
新的主页地址是: http://www.mybatis.org
另外,MyBatis3.0.1的正式版已经在http://code.google.com/p/mybatis/可以下载了。
4月17日,iBatis3.0的GA版本的候选版本(candidate)已经在主页上公布出来了(ibatis.apache.org)
根据其描述,只有PMC的投票才会被真正的计算。但是同时又补充说明,其实很多PMC的投票是参考社区用户的投票结果的。
所以也鼓励大家发表自己的看法,并期待大家的反馈。
原文:
After well over a year of development and testing effort, iBATIS 3.0 is now ready for prime time. I've uploaded the new bundles for iBATIS 3 Core and Migrations. It is this release that we'll vote on for GA status. I'll leave the vote open for a good while (maybe a couple of weeks), just to give everyone a chance to try it out. As usual, only PMC votes actually count, but many PMC members will vote based on the community votes. So please offer your feedback. Otherwise, enjoy iBATIS 3.0!
在最上面添加了标签页,目前分了6大块:Java,SOA,.Net,TEST,AJAX,TSSJs
主框架也进行了调整,从原来的2列变成了3列,将原来在右边的要点移到了左边。文章占据了中间最大片的面积。最右边是新闻。
感觉整个界面比原来的色调要清新明亮,版块分割的更清楚。
出自伏尔泰的名句。在许多优秀的程序员的编程习惯中,寻找解决方案就如同是寻找女友一般,总是力求完美,如果一个解决方案不够完美的话,那么宁可舍弃也不会降低标准而采用。但是这样的结果很有可能会是一叶障目,不见森林。
举个实际的例子来说,tapestry5的服务动态加载就是一个典型。
大家知道,在Tapestry中,页面模板的动态加载一直是其的一个宣传点。也就是说,页面模板如果修改了,不需要重新启动应用服务器,修改即时生效。但是,Tapestry却一直没有实现服务的动态加载,也就是说服务的类修改了,就必须重新启动服务器。为什么呢,因为追求完美。
其实很久以前在Tapestry的作者的头脑中,包括许多的用户都想在Tapestry中加入服务动态加载的功能,但是最后作者却放弃了。原因是因为,在Tapestry的架构中,服务比页面要复杂的多,服务不是单独存在的,他会被Inject到许多其他的服务,而这些服务又会被inject到其他的服务中,最后导致你很难界定哪些类需要被重新加载。
但是最近有一个人提出一个建议,为什么要实现完整的动态加载呢。其实只实现服务的实现类的动态加载就已经很好了。
确实,如果保持接口不变,只在接口具体实现修改的时候加载,确实大大简化了问题,因为这样变化范围就被限定在单个类之内了。虽然这样并不完美,因为如果改变了对外接口,比如,添加了一个新的方法,或者改变了方法的签名,还是需要重新启动服务器。可是这样也已经在很大程度上提升了用户的使用满意度。
正如有句古语所说:退一步海阔天空。也在此提醒自己在以后的开发过程中,尽量拓展自己的思维,不要一下子钻到牛角尖里。
2月25日,IBatis3发布了Beta10,在主页上宣称,一个月来Beta9的公测都没有收到真正的Bug提交。
所有收到的问题都是新的功能要求。
所以,如果没有意外的话,Beta10将会用于投票表决是否作为GA(General Available)版本。
在教程的第四部分(http://www.blogjava.net/usherlight/archive/2009/06/20/283396.html)我们已经粗略地介绍了Tapestry的前缀,其中提及最常用的两种literal和prop。这里顺便再介绍一下其他的几个前缀.
1、context,这个经常在引用图片的时候用到。比如:<img src="${context:images/icon.png}"/> 这样就能够在页面上显示在web-inf/images/icon.png
2、message,这个是需要I18N的应用必用的前缀。${message:some-key}
3、var,用于定义生成一个临时变量。比如:<li t:type="loop" source="1..10" value="var:index">${var:index}</li>。如果没有加这个前缀var:的话,就需要在页面类中添加一个property: index以及相应的getter和setter。
我们这次介绍自定义一个cycle前缀,这个可以用于在表格中显示不同背景的行(也就是斑马条)。
比如:
<t:grid .... rowClass="cycle:line1,line2">
...
</t:grid>
或者
<t:loop ...>
<div class="${cycle:line1,line2}">aaaa</div>
</tloop>
自定义prefix一般来说是3个步骤,
1、定义一个BindingFactory,这个需要实现BindingFactory接口
2、定义一个Binding继承AbstractBinding
3、注册这个Binding
看一下具体的prefix的类:
1 import java.util.ArrayList;
2 import java.util.List;
3![](/Images/OutliningIndicators/None.gif)
4 import org.apache.tapestry5.Binding;
5 import org.apache.tapestry5.BindingConstants;
6 import org.apache.tapestry5.ComponentResources;
7 import org.apache.tapestry5.ioc.Location;
8 import org.apache.tapestry5.services.BindingFactory;
9 import org.apache.tapestry5.services.BindingSource;
10![](/Images/OutliningIndicators/None.gif)
11![](/Images/OutliningIndicators/ExpandedBlockStart.gif) /** *//**
12 * Implementation of the cycle: binding prefix -- we parse list of bindings
13 * and generate delegate bindings for each element<br>
14 * default binding is literal, other bindings can be used by specifying prefix.<br>
15 * example: "cycle:prop:name,prop:lastName,sth,sth else"
16 */
17![](/Images/OutliningIndicators/ExpandedBlockStart.gif) public class CycleBindingFactory implements BindingFactory {
18 private final BindingSource _bindingSource;
19![](/Images/OutliningIndicators/InBlock.gif)
20![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif) public CycleBindingFactory(BindingSource source) {
21 this._bindingSource = source;
22 }
23
24 public Binding newBinding(String description, ComponentResources container, ComponentResources component,
25 String expression, Location location)
26![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif) {
27 List<Binding> delegates = new ArrayList<Binding>();
28 String[] bindingNames = expression.split(",");
29![](/Images/OutliningIndicators/InBlock.gif)
30![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif) for (String bindingName : bindingNames) {
31 String defaultBinding = BindingConstants.LITERAL;
32![](/Images/OutliningIndicators/InBlock.gif)
33 Binding binding = _bindingSource.newBinding(description, container, component, defaultBinding, bindingName, location);
34 delegates.add(binding);
35 }
36
37 CycleBinding cycleBinding = new CycleBinding(delegates);
38 container.addPageLifecycleListener(cycleBinding);
39
40 return cycleBinding;
41 }
42 }
1 import java.util.List;
2![](/Images/OutliningIndicators/None.gif)
3 import org.apache.tapestry5.Binding;
4 import org.apache.tapestry5.internal.bindings.AbstractBinding;
5 import org.apache.tapestry5.runtime.PageLifecycleListener;
6![](/Images/OutliningIndicators/None.gif)
7![](/Images/OutliningIndicators/None.gif)
8![](/Images/OutliningIndicators/ExpandedBlockStart.gif) public class CycleBinding extends AbstractBinding implements PageLifecycleListener {
9 private final List<Binding> delegates;
10 private int index = 0;
11![](/Images/OutliningIndicators/InBlock.gif)
12![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif) public CycleBinding(List<Binding> delegates) {
13 this.delegates = delegates;
14 }
15![](/Images/OutliningIndicators/InBlock.gif)
16![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif) public Object get() {
17 Object ret = delegates.get(index).get();
18 index ++;
19 if(index>=delegates.size()) index = 0;
20 return ret;
21 }
22
23 @Override
24![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif) public boolean isInvariant() {
25 return false;
26 }
27
28 @Override
29![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif) public Class<Object> getBindingType() {
30 return Object.class;
31 }
32![](/Images/OutliningIndicators/InBlock.gif)
33![](/Images/OutliningIndicators/InBlock.gif)
34![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif) public void containingPageDidDetach() {
35 index=0;
36 }
37![](/Images/OutliningIndicators/InBlock.gif)
38![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif) public void containingPageDidAttach() {/**//*not interested*/}
39
40![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif) public void containingPageDidLoad() {/**//*not interested*/}
41 }
Binding和BindingFactory写好了,注册后就可以使用了,注册的过程是在AppModel中添加以下一段代码:
1 public static void contributeBindingSource(
2 MappedConfiguration<String, BindingFactory> configuration,
3 BindingSource bindingSource
4 )
5![](/Images/OutliningIndicators/ExpandedBlockStart.gif) {
6 configuration.add("cycle",new CycleBindingFactory(bindingSource));
7 }
1. 先开始看SpringSide吧。
主要看寒冬日志版3.2.1开始有的JMS演示和WebService演示
一直觉得EJB好像已经日薄西山了,但是实际上生命力还挺顽强的。
另外,还有Web Service, SOA这个概念还是有人相信的。
所以,延伸项目有:JMS, MQ
我记得好像是Appfuse的作者曾经这样评价过Tapestry:只要你真正掌握了Tapestry,你的开发效率将会得到极大的提高。为什么呢?我认为他这样说的一个重要原因就是Tapestry的组件机制。Tapestry提供了非常便利的组件定义机制,随着Tapestry的组件不断积累,Tapestry的开发将会变得越来越简单。
本文就用一个实例来看一下Tapestry中是如何添加一个自定义组件的。
Tapestry的内置组件只提供了checkbox,而且只能返回一个boolean,用于表明是否被选中。
比如,要进行一个群众喜爱的水果调查,选项有: 苹果,葡萄,桃子,香蕉...,就需要对应每个选项设置一个布尔型变量,显得比较繁琐。
这里我们将添加一个组件用于将一组checkbox集中起来返回一个逗号分隔的字符串值。
通过查看Tapestry中的checkbox的源码(已经附在文章的后面)可以知道,Tapestry可以很容易地通过Request来获取Form中的变量的值。
遇到的问题:
Tapestry的checkbox组件不允许设置相同的name,如果name相同,Tapestry会自动在name后面添加后缀来使之不同。
If a component renders multiple times, a suffix will be appended to the to id to ensure uniqueness(http://tapestry.apache.org/tapestry5.1/tapestry-core/ref/org/apache/tapestry5/corelib/components/Checkbox.html)。如果各checkbox的name不同,我们无法通过request来获得一组checkbox的值。
思路:
在页面模板中不使用tapestry的checkbox组件,而使用Html的checkbox,这样可以避免tapestry自动修改checkbox的name。
添加一个新的tapestry组件,来映射接受所有同名的checkbox的值,并把值返回给tapestry页面中对应的变量。这个组件需要有一个属性,这个属性的值就是所有同组checkbox的name,这样,这个组件就可以通过Request来获取所有相同name的checkbox的值。
代码:
1 public class CheckBoxGroup extends AbstractField {
2
3 @SuppressWarnings("unused")
4 @Parameter(required = true, autoconnect = true)
5 private String value;
6
7 @Parameter(required = true, autoconnect = true)
8 private String groupName;
9
10 @Inject
11 private Request request;
12
13 @SuppressWarnings("unused")
14 @Mixin
15 private RenderDisabled renderDisabled;
16
17 @Inject
18 private ComponentResources resources;
19
20 @BeginRender
21 void begin(MarkupWriter writer)
22 {
23 writer.element("input", "type", "checkbox",
24 "name", groupName,
25 "id", getClientId(),
26 "style", "display:none");
27
28 resources.renderInformalParameters(writer);
29
30 decorateInsideField();
31 }
32
33 @AfterRender
34 void after(MarkupWriter writer)
35 {
36 writer.end(); // input
37 }
38
39 @Override
40 protected void processSubmission(String elementName)
41 {
42 String elementValue = "";
43 String[] valueArray = request.getParameters(groupName);
44 if ( valueArray != null && valueArray.length > 0 ) {
45 elementValue = valueArray[0];
46 for ( int i = 1; i < valueArray.length; i ++ ) {
47 elementValue += "," + valueArray[i];
48 }
49 }
50 value = elementValue;
51 }
52 }
组件的使用:
-----tml------
<t:CheckBoxGroup t:groupName="literal:bookId" t:value="selectedBooks"/>
<t:loop source="bookList" value="book" encoder="encoder">
<div><input type="checkbox" name="bookId" value="${book.id}"/> ${book.name}</div>
</t:loop>
注意checkBoxGroup的groupName和其他checkbox的name必须一致,checkBoxGroup的value的值就是页面中的变量名
-----java-----
@SuppressWarnings("unused")
@Property
private final ValueEncoder<Book> encoder = new ValueEncoder<Book>() {
public String toClient(Book value) {
return String.valueOf(value.getId());
}
public Book toValue(String clientValue) {
return bookDao.getBook(Integer.parseInt(clientValue));
}
};
public List<Book> getBookList() {
return bookDao.getBooks();
}
@SuppressWarnings("unused")
@Property
private Book book;
@SuppressWarnings("unused")
@Property
private String selectedBooks;
在tapestry5中,在页面之间传递基本有3种方法
1、存放在Session中
2、使用@Persist进行持久化
3、使用页面context来传递参数。
其中1和2都需要将数据存放在Session中,相对来说系统的开销比较大。尤其是多用户高并发情况下,对于性能可能会有一定的影响。
使用页面Context来传递则需要在开发时写一些代码,增加了一些开发量,显得没有前两种方法方便。
第3种方法的实现是需要在页面中添加onActivate和onPassivate方法来完成页面参数的传递。
我们先来看一下其背后的故事。
举个例子,比如说我们有两个页面,第一个是查询条件输入页面input,另一个是查询结果输出页面output。input页面中有两个查询条件,起始时间dateFrom和终止时间dateTo
在Input.java中,我们可以很直观地这样写:
@InjectPage
private Output output;
@Property
private String dateFrom;
@Property
private String dateTo;
Object onFormSubmit() {
output.setDateFrom(dateFrom);
output.setDateTo(dateTo);
return output;
}
首先使用注解注入output页面,然后在表单的提交事件中,返回output,这样就在程序中定义了返回页面,而不是使用配置文件的方式。
但是这样的实现却不能正确运行,原因是因为Tapestry5的使用了页面池技术,页面在每次渲染前都是从页面池中随机获取一个页面,而从页面池中取得的页面,所有的属性都是被清空了的。
也就是说在上例中,虽然我们注入了output页面,但是此页面马上就被放入了页面池,而且其中的属性值马上就被清空了。这就是引入onActivate和onPassivate这丙个方法的原因。tapestry5在清空属性前会首先查看是否包含onPassivate方法,如果有,就把其返回值保存起来,然后从页面池中取得页面后,再把刚才保存的值作为参数传递给onActivate方法。
这就是方法3的基本原理,但是无论是在官方的文档或是示例或者网上其他的应用中,可以发现大部分都是使用单个参数的,比如说id。这也很容易理解,因为onPassivate的方法的返回值只能有一个。
在Tapestry5的官方文档中,只有一句非常简要的话介绍了如果传递多个文档的方法: The activation context may consist of a series of values, in which case the return value of the method should be an array or a List. (参见:http://tapestry.apache.org/tapestry5.1/guide/pagenav.html)。
但是这并不是说只要在onPassivate中把参数的值加入到List中,返回一个List,而在onActivate中接受一个List参数,然后就可以得到其中的参数了,因为Tapestry5把参数传给onActivate的方法其实是通过将参数作为HttpRequest中的参数的。如果试图使用上述方法就是得到一个“无法将List转换成String的错误”
所以方法应该是这样的,在Output中:
private List<String> paramList;
public void setParamList(List<String> paramList) {
this.paramList = paramList;
}
public List<String> getParamList() {
return paramList;
}
List<String> onPassivate() {
return paramList;
}
void onActivate(String dateFrom, String dateTo) {
this.dateFrom = dateFrom;
this.dateTo = dateTo;
}
private String dateFrom;
private String dateTo;
在Input页面中,需要把onFormSubmit改一下:
Object onFormSubmit() {
List<String> list = new ArrayList<String>();
output.setParamList(list);
return output;
}
其中,需要注意的是output中的onActivate方法,基参数的顺序必须和List中放入的参数顺序一致。
在上一篇中我们研究了如何实现SpringSecurity中Jsp Tag的<security:authorize ifAllGranted="ROLE_SUPERVISOR">的功能。这一次我们一起研究一下如何实现在Tapestry5.1中添加一个Filter来对所有的操作进行权限的过滤控制。
在SpringSecurity中,我们一般是在application-context.xml中,添加一个SpringSecurity的Filter,然后在另外一个xml中详细配置如何根据Url的规则进行权限的控制。而Tapestry的哲学是尽量减少Xml中的配置(其IOC容器也基本上是借鉴Guice而不Spring的),所以我们也是在代码中实现权限规则的控制。
总体上来看,可以用两种方式来实现url规则,一种是Request级别的Filter,一种是页面组件级别的Filter,如果是Request级别的话,可以从Request对象中获取Url路径,这样就与SpringSecurity基本一样了。本文主要介绍页面组件级别的Filter,从中我们也可以体会到Tapestry5.1中的IOC容器的强大和便利。
这就是Filter的代码,这个Filter必须实现ComponentRequestFilter接口。值得注意的是其构造函数所需要用到的4个参数,这4个参数都是Tapestry5本身自有的服务,所以我们什么也不用做,Tapestry5自动会将服务的实例注入进来,这就是Tapestry-IOC的威力。
ComponentRequestFilter接口一共有4个方法需要实现,具体代码如下:
1 public class RequiresLoginFilter implements ComponentRequestFilter {
2
3 private final PageRenderLinkSource renderLinkSource;
4
5 private final ComponentSource componentSource;
6
7 private final Response response;
8
9 private final ApplicationStateManager appStateManager;
10
11 public RequiresLoginFilter(PageRenderLinkSource renderLinkSource,
12 ComponentSource componentSource, Response response,
13 ApplicationStateManager appStateManager
14 ) {
15 this.renderLinkSource = renderLinkSource;
16 this.componentSource = componentSource;
17 this.response = response;
18 this.appStateManager = appStateManager;
19 }
20
21 public void handleComponentEvent(
22 ComponentEventRequestParameters parameters,
23 ComponentRequestHandler handler) throws IOException {
24
25 if (dispatchedToLoginPage(parameters.getActivePageName())) {
26 return;
27 }
28
29 handler.handleComponentEvent(parameters);
30
31 }
32
33 public void handlePageRender(PageRenderRequestParameters parameters,
34 ComponentRequestHandler handler) throws IOException {
35 if (dispatchedToLoginPage(parameters.getLogicalPageName())) {
36 return;
37 }
38 handler.handlePageRender(parameters);
39
40 }
41
42 private boolean dispatchedToLoginPage(String pageName) {
43 Component page = componentSource.getPage(pageName);
44
45 if (page.getClass().isAnnotationPresent(RequiresLogin.class)) {
46 if ( ! appStateManager.exists(Authentication.class)) {
47 redirect();
48 return true;
49 }
50 Authentication auth = appStateManager.get(Authentication.class);
51 if ( auth == null ) {
52 redirect();
53 return true;
54 }
55
56 if ( ! auth.isLoggedIn()) {
57 redirect();
58 return true;
59 }
60
61 RequiresLogin requireLogin = page.getClass().getAnnotation(
62 RequiresLogin.class);
63 String ifNotGranted = requireLogin.ifNotGranted();
64 String ifAllGranted = requireLogin.ifAllGranted();
65 String ifAnyGranted = requireLogin.ifAnyGranted();
66 boolean permitted = auth.checkPermission(ifNotGranted, ifAllGranted, ifAnyGranted);
67 if ( ! permitted ) {
68 return true;
69 }
70 }
71
72 return false;
73 }
74
75 private void redirect() {
76 Link link = renderLinkSource.createPageRenderLink("Logout");
77
78 try {
79 response.sendRedirect(link);
80 } catch (Exception e) {
81 }
82 }
83
84 }
在ComponentRequestFilter中,我们无法使用@SessionState注解来直接注入Session中的变量,但是我们可以通过ApplicationStateManager来取得。
现在我们需要把刚定义的Filter注册到系统中,很简单,只要在AppModule中添加以下函数就行了:
1 public static void contributeComponentRequestHandler(
2 OrderedConfiguration<ComponentRequestFilter> configuration) {
3 configuration.addInstance("RequiresLogin", RequiresLoginFilter.class);
4 }
5
从本例子中我们可以看到Tapesty Ioc容器使用的便利性,也认识到了Ioc容器在Tapestry体系中的重要性
IBatis2中提供了3种DataSource的配置:JNDI, Apache DBCP, IBatis自带的SimpleDataSource。但在IBatis3中只提供了两种DataSource: UNPOOLED, POOLED。
如果要实现自定义的DataSource,就需要通过扩展DataSourceFactory。本文就演示一下这个过程。
准备工作:Connection Pool的选择,通过搜索发现目前比较流行的免费数据库连接池主要有3种:Apache DBCP, C3P0, Proxool。
看了一下,Proxool的最新版本是0.9.1(2008-08-23), C3P0的最新版本是0.9.1.2(2007-05-21), DBCP最新版本是1.2.2(2007-04-04)
好像这3个项目都已经挺长时间没有更新了。但是总体评价上C3P0无论从稳定上还是效率上都要好一点。
(具体这3个项目谁更优秀,并不是本文的重点,本文主要是介绍一下如何在IBatis3中自定义数据源)
大致步骤:
1、实现org.apache.ibatis.datasource.DataSourceFactory接口,主要是2个方法
a、public DataSource getDataSource() 如何具体地得到一个数据源
b、public void setProperties(Properties properties) 如何设置数据源的参数属性
2、实现javax.sql.DataSource,这个就是提供给DataSourceFactory的实例
3、在IBatis3中引用新加入的数据源
1. 从代码中可以看出,IBatis3与IBatis2不同,不再通过一个Configuration类来进行数据源属性的设置,而是使用反射机制直接调用数据源的方法来设置参数。
这就要求配置文件中的参数名称必须与数据源类中的方法名匹配.
1 public class C3p0DataSourceFactory implements DataSourceFactory {
2
3 private DataSource dataSource;
4
5 public C3p0DataSourceFactory() {
6 dataSource = new C3p0DataSource();
7 }
8
9 public DataSource getDataSource() {
10 return dataSource;
11 }
12
13 public void setProperties(Properties properties) {
14 Properties driverProperties = new Properties();
15 MetaObject metaDataSource = MetaObject.forObject(dataSource);
16 for (Object key : properties.keySet()) {
17 String propertyName = (String) key;
18 if (propertyName.startsWith(DRIVER_PROPERTY_PREFIX)) {
19 String value = properties.getProperty(propertyName);
20 driverProperties.setProperty(propertyName
21 .substring(DRIVER_PROPERTY_PREFIX_LENGTH), value);
22 } else if (metaDataSource.hasSetter(propertyName)) {
23 String value = (String) properties.get(propertyName);
24 Object convertedValue = convertValue(metaDataSource,
25 propertyName, value);
26 metaDataSource.setValue(propertyName, convertedValue);
27 } else {
28 throw new DataSourceException("Unkown DataSource property: "
29 + propertyName);
30 }
31 }
32 if (driverProperties.size() > 0) {
33 metaDataSource.setValue("driverProperties", driverProperties);
34 }
35 }
36
37 @SuppressWarnings("unchecked")
38 private Object convertValue(MetaObject metaDataSource, String propertyName,
39 String value) {
40 Object convertedValue = value;
41 Class targetType = metaDataSource.getSetterType(propertyName);
42 if (targetType == Integer.class || targetType == int.class) {
43 convertedValue = Integer.valueOf(value);
44 } else if (targetType == Long.class || targetType == long.class) {
45 convertedValue = Long.valueOf(value);
46 } else if (targetType == Boolean.class || targetType == boolean.class) {
47 convertedValue = Boolean.valueOf(value);
48 }
49 return convertedValue;
50 }
51
52 private static final String DRIVER_PROPERTY_PREFIX = "driver.";
53 private static final int DRIVER_PROPERTY_PREFIX_LENGTH = DRIVER_PROPERTY_PREFIX
54 .length();
55
56 }
57
2. 数据源类,其中的一堆setter就是用于设置属性的。
1 public class C3p0DataSource implements DataSource {
2
3 private ComboPooledDataSource dataSource;
4 public C3p0DataSource() {
5 this.dataSource = new ComboPooledDataSource();
6 }
7
8 public Connection getConnection() throws SQLException {
9 return dataSource.getConnection();
10 }
11
12 public Connection getConnection(String username, String password)
13 throws SQLException {
14 return dataSource.getConnection(username, password);
15 }
16
17 public PrintWriter getLogWriter() throws SQLException {
18 return dataSource.getLogWriter();
19 }
20
21 public int getLoginTimeout() throws SQLException {
22 return dataSource.getLoginTimeout();
23 }
24
25 public void setLogWriter(PrintWriter out) throws SQLException {
26 dataSource.setLogWriter(out);
27 }
28
29 public void setLoginTimeout(int seconds) throws SQLException {
30 dataSource.setLoginTimeout(seconds);
31 }
32
33
34 public synchronized void setDriver(String driver) {
35 try {
36 dataSource.setDriverClass(driver);
37 } catch (Exception e) {
38 }
39 }
40
41 public void setUrl(String url) {
42 dataSource.setJdbcUrl(url);
43 }
44
45 public void setUsername(String username) {
46 dataSource.setUser(username);
47 }
48
49 public void setPassword(String password) {
50 dataSource.setPassword(password);
51 }
52
53 public void setInitialPoolSize(int initialPoolSize) {
54 dataSource.setInitialPoolSize(initialPoolSize);
55 }
56
57 public void setMaxPoolSize(int maxPoolSize) {
58 dataSource.setMaxPoolSize(maxPoolSize);
59 }
60
61 public void setMinPoolSize(int minPoolSize) {
62 dataSource.setMinPoolSize(minPoolSize);
63 }
64
65 public void setPreferredTestQuery(String preferredTestQuery) {
66 dataSource.setPreferredTestQuery(preferredTestQuery);
67 }
68
69 public void setPoolPingQuery(String poolPingQuery) {
70 dataSource.setPreferredTestQuery(poolPingQuery);
71 }
72 }
3. 在配置文件Configuration.xml中,可以先定义数据源的别称,然后就象POOLED和UNPOOLED一样使用别称来引用数据源。
<Configuration>
...
<typeAlias>
<typeAlias type="com.test.datasource.C3p0DataSourceFactory" alias="C3P0"/>
</typeAlias>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="C3P0">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="poolPingQuery" value="${pingquery}"/>
</dataSource>
</environment>
</environments>
...
<Configuration>
最近在使用tapestry5.1.0.5开发项目的时候,突然报错:
Exception in thread "main" java.lang.ClassFormatError: Invalid length 561 in LocalVariableTable in class file
在网上搜索后,发现有人也有同样的错误,解决方法有两种:
http://mail-archives.apache.org/mod_mbox/tapestry-users/200909.mbox/%3Cecd0e3310909040909id5275beld935fc60d54d490a@mail.gmail.com%3E
其中一个人的错误原因是在其类路径中有不同版本的javassists的jar文件。
另一个的解决方法是使用eclipse自带的jdk来编译java类。
而我自己仔细检查了类路径中的文件,并没有重复的javassists,不过我觉得问题应该就在javassists上,
因为这显然是javassists在操作class文件时报的错误,
我去网上搜索这方面的信息,发现有好几个人都和我一样在使用javassists3.11.0GA版本的时候,会出现这个错误。
后来,我改用Tapestry5中自带的javassists3.9.0GA后,问题消失了。
这次经验教训是并不是所有最新的东西都是最好的。合适的才是最好的。
IBatis3的Beta8版本已经发布了,在官方网站上声称目前的版本已经非常稳定,只有4个已知的问题,其中2个是非功能性的。作者宣称,这样的状况使它对于近期发布GA版本充满信心。
那么IBatis3与IBatis2相比,究竟变化在哪里呢?
最重要的变化是IBatis3中引入了接口绑定(Interface Binding)的概念。在IBatis2中,没有应用Java5的泛型,所以需要大量使用强制类型转换,比如:
Employee employee = (Employee)sqlMapper.queryForList("getEmployee", 5);
//...and...
List employees = sqlMapper.queryForList("listAllEmployees");
但是在IBatis3中,方法改变成:
MapperFactory factory = someConfiguration.buildMapperFactory();
EmployeeMapper employeeMapper = factory.getMapper (EmployeeMapper.class);
Employee emp = empMapper.getEmployee(5);
//...and...
List<Employee> employees = empMapper.listAllEmployees();
所以IBatis3至少需要使用Java5以上的版本。上面代码中,EmployeeMapper是一个自定义的接口(注意,开发人员只需要定义一个接口,不需要提供具体的实现)
public interface EmployeeMapper {
Employee getEmployee (int employeeId);
List<Employee> listAllEmployees();
}
这样就行了,IBatis会自动为你生成接口的具体实现。是不是感觉有点酷?
Tapestry中并没有类似于Spring Security这样的专门的权限框架。对此Tapestry的作者Lewis认为主要是用户对于权限的要求实在太多变化了。他认为很难抽象出一个通用的权限框架来满足所有的用户,所以他干脆就不费事去做这件事了。但其实我们很容易就能利用Tapestry已有的工具来完成类似于SpringSecurity的功能。
本文主要介绍如何实现类似于SpringSecurity的jsp tag的功能。在Tapestry中,利用Components实现这一点非常容易。
其基本原理是Tapestry5中一个页面或者组件的渲染生成过程是基于一个状态机和队列完成的。这样,渲染生成过程就被细分成了很多个小模块,我们可以非常容易地覆写这些小模块。具体内容详见官方文档:http://tapestry.apache.org/tapestry5.1/guide/rendering.html。如果权限校验不通过,我们就可以控制不显示组件的内容。
我们这里就是主要依赖这个过程来实现在页面这一层面对权限进行校验和控制。
代码主要包含两大部分,一个组件和一个用于权限控制的服务。
参考了Tapestry-Spring-Security的实现,我也将组件命名为IfRole(当然,我们也可以和Tapestry-Spring-Security一样,也再生成一个IfLoggedIn组件)。权限控制的服务我命名为:AuthenticationService。
主要的实现思路:
将AuthenticationService申明为SessionState变量。这样这个变量就可以在所有的页面和组件之间很方便地共享了。一般情况下,是在登录页面对AuthenticationService进行赋值,而在退出页面清空AuthenticationService这个变量。
代码(这部分代码完全根据应用的需求进自行更改):
AuthenticationService的代码:
![](/Images/OutliningIndicators/ExpandedBlockStart.gif) public class AuthenticationService {
private List<String> privilegeList;
// privilegeList 的getter and setter
![](/Images/OutliningIndicators/InBlock.gif)
public boolean checkPermission(String ifNotGranted, String ifAllGranted,
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif) String ifAnyGranted) {
if (((null == ifAllGranted) || "".equals(ifAllGranted))
&& ((null == ifAnyGranted) || "".equals(ifAnyGranted))
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif) && ((null == ifNotGranted) || "".equals(ifNotGranted))) {
return false;
}
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif) if ((null != ifNotGranted) && !"".equals(ifNotGranted)) {
StringTokenizer st = new StringTokenizer(ifNotGranted, ",");
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif) while (st.hasMoreTokens()) {
String value = st.nextToken();
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif) if (privilegeList.contains(value)) {
return false;
}
}
}
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif) if ((null != ifAllGranted) && !"".equals(ifAllGranted)) {
StringTokenizer st = new StringTokenizer(ifAllGranted, ",");
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif) while (st.hasMoreTokens()) {
String value = st.nextToken();
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif) if (!privilegeList.contains(value)) {
return false;
}
}
}
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif) if ((null != ifAnyGranted) && !"".equals(ifAnyGranted)) {
StringTokenizer st = new StringTokenizer(ifAnyGranted, ",");
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif) while (st.hasMoreTokens()) {
String value = st.nextToken();
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif) if (privilegeList.contains(value)) {
return true;
}
}
return false;
}
![](/Images/OutliningIndicators/InBlock.gif)
return true;
}
}
IfRole的代码(这个类需要放在Components目录下):
![](/Images/OutliningIndicators/ExpandedBlockStart.gif) public class IfRole {
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif) /** *//**
* A comma-separated list of roles is supplied to one or more of the
* following parameters. If none are supplied, the default behavior is to
* forbid access. Behavior should be self-explanatory.
*/
@Parameter(required = false, defaultPrefix = "literal")
private String ifAllGranted;
![](/Images/OutliningIndicators/InBlock.gif)
@Parameter(required = false, defaultPrefix = "literal")
private String ifAnyGranted;
![](/Images/OutliningIndicators/InBlock.gif)
@Parameter(required = false, defaultPrefix = "literal")
private String ifNotGranted;
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif) /** *//**
* An alternate {@link Block} to render if the test parameter is false. The default, null, means
* render nothing in that situation.
*/
@Parameter(name = "else")
private Block elseBlock;
![](/Images/OutliningIndicators/InBlock.gif)
private boolean test;
@SessionState
private AuthenticationService auth;
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif) private boolean checkPermission() {
return auth.checkPermission(ifNotGranted, ifAllGranted, ifAnyGranted);
}
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif) void setupRender() {
test = checkPermission();
}
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif) /** *//**
* Returns null if the test method returns true, which allows normal
* rendering (of the body). If the test parameter is false, returns the else
* parameter (this may also be null).
*/
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif) Object beginRender() {
return test ? null : elseBlock;
}
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif) /** *//**
* If the test method returns true, then the body is rendered, otherwise not. The component does
* not have a template or do any other rendering besides its body.
*/
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif) boolean beforeRenderBody() {
return test;
}
}
示例:
1. 在登录页面:
@SessionState
private Authentication auth;
......
// if user name and password is valid:
auth.setPrivliegeList(.....);
2. 在需要权限控制的页面模板中:
<t:ifRole ifAllGranted="admin">
administrator can see this block
</t:ifRole>
与现在最流行的SSH相比较,Tapestry能够完全替代其中Struts2和Spring,但是他还是需要一个ORM的框架。IBatis由于比较低的学习曲线,也受到很多人的喜爱。尤其是在IBatis3中引入了许多新的概念和想法,使用更加安全和便利。
本文主要介绍如何将Tapestry5.1和IBatis3进行整合。
简要步骤:
1. 准备工作
2. 数据库的建立
3. POJO的建立
4. IBatis相关配置文件的创建
5. Tapestry相关代码的完成
概要说明:
1、准备工作。这一部分是比较简单的,Eclipse之类的开发环境是必需的。Tapestry5.1、IBatis3(目前还是Beta7)、数据库(我使用的是MySql)的下载安装。
2、数据库的建立,由于是示例,所以数据库的建立也非常简单,只有一张User表,3个字段,Id,Name,Password
3、com.sample.User类,对应数据库表的3个字段,生成User类
4、IBatis配置文件:Configuration.xml,UserMapper.xml,jdbc.properties的生成, 前两个必需,最后一个可选.
5、在AppModule里,使用build方法, 添加服务生成IBatis3的SqlSessionFactory, 在需要使用SqlSessionFactory的地方,使用@InjectService注入即可
详细说明:
1、大家到各自的网站上下载相应的包好了。我只罗列一下我所用到的Lib:
antlr-runtime-3.1.1.jar
commons-codec-1.3.jar
commons-lang-2.4.jar
ibatis-3-core-3.0.0.216.jar
javassist.jar
log4j-1.2.14.jar
mysql-connector-java-5.0.5.jar
slf4j-api-1.5.10.jar
slf4j-log4j12-1.5.10.jar
stax2-api-3.0.1.jar
tapestry-core-5.1.0.5.jar
tapestry-ioc-5.1.0.5.jar
tapestry5-annotations-5.1.0.5.jar
woodstox-core-lgpl-4.0.7.jar
2、Create Table
DROP TABLE IF EXISTS `test`.`user`;
CREATE TABLE `test`.`user` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(45) NOT NULL,
`password` varchar(45) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
3、
package com.sample.model;
public class User {
private long id;
private String name;
private String password;
// getter and setter ....
}
4、我把Configuration.xml和UserMapper.xml都放在src目录下,这样在部署的时候,就是生成在classes,也就是类路径的根目录下。
Configuration.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//ibatis.apache.org//DTD Config 3.0//EN"
"http://ibatis.apache.org/dtd/ibatis-3-config.dtd">
<configuration>
<properties resource="jdbc.properties">
</properties>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="poolPingEnabled" value="${pingenable}"/>
<property name="poolPingQuery" value="${pingquery}"/>
<property name="poolPingConnectionsNotUsedFor" value="${pingnotusetime}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="UserMapper.xml"/>
</mappers>
</configuration>
UserMapper.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"
"http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd">
<mapper namespace="com.sample.model.UserMapper">
<select id="selectUser" parameterType="int" resultType="com.sample.model.User">
select * from user where id = #{id}
</select>
</mapper>
jdbc.properties:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost/test?autoReconnect=true
jdbc.username=root
jdbc.password=root
pingenable=true
pingquery=SELECT 1
pingoldertime=0
pingnotusetime=3600000
5、
package com.sample.web.services;
public class AppModule {
public static SqlSessionFactory buildSqlSessionFactory() {
try {
String resource = "Configuration.xml";
Reader reader = Resources.getResourceAsReader(resource);
return new SqlSessionFactoryBuilder().build(reader);
} catch (Exception e) {
logger.warn("failed to build SqlSessionFactory: ", e);
return null;
}
}
private static Logger logger = LoggerFactory.getLogger(AppModule.class);
}
package com.sample.model;
public interface UserMapper {
public User selectUser(int id);
}
package com.pc.sample.web.pages;
public class Layout {
@InjectService("SqlSessionFactory")
private SqlSessionFactory sqlMapper;
public String getUserName() {
if ( sqlMapper == null ) {
return "null-mapper";
}
SqlSession session = sqlMapper.openSession();
try {
UserMapper userMapper = session.getMapper(UserMapper.class);
if ( userMapper == null ) {
return "null-userMapper";
}
User user = userMapper.selectUser(1);
if ( user == null ) {
return "null-user";
}
return user.getName();
} catch (Exception e) {
return "exception-" + e.getMessage();
} finally {
session.close();
}
}
}
几个注意事项:
1,
因为我的IBatis的配置文件Configuration.xml是放在类路径的根目录下,所以在初始化SqlSessionFactory的时候,直
接用String resource =
"Configuration.xml";就行了,否则需要添加相应的路径,比如:把Configuration.xml与User类放在一起,也就是在
com.sample.model这个package中,那么就要写成:String resource =
"com/sample/model/Configuration.xml";
同样,在Configuration.xml中,指定UserMapper.xml的规则也是这样的。
2,UserMapper的使用。Mapper的使用是IBatis3中才有的新功能,也是IBatis用户指南中推荐使用的方式。因为这样使用的话,就完全避免了类型的强制转换,实现了类型安全。
需要注意的是UserMapper只是一个接口。我们不需要提供这个接口的具体实现。IBatis3会自动生成一个具体的实例。
其中的方法名必须与UserMapper.xml中的select语句的id一样。在我的例子中是selectUser.
另外,此方法的返回值的类型必须与UserMapper.xml中配置的returnType一致。
最后要提醒的是UserMapper.xml中的namespace必须是UserMapper的全类名,在本例中就是com.sample.model.UserMapper
here is a summary of key features in Spring 3.0 overall:
* Spring expression language (SpEL): a core
expression parser for use in bean definitions, allowing for references
to nested bean structures (e.g. properties of other beans) as well as
to environmental data structures (e.g. system property values) through
a common #{…} syntax in property values.
* Extended support for annotation-based components:
now with the notion of configuration classes and annotated factory
methods (as known from Spring JavaConfig). Spring also allows for
injecting configuration values through @Value expressions now,
referring to configuration settings via dynamic #{…} expressions or
static ${…} placeholders.
* Powerful stereotype model: allows for creating
'shortcut' annotations through the use of meta-annotations, e.g. for
default scopes and default transactional characteristics on custom
stereotypes. Imagine a custom @MyService annotation indicating
@Service, @Scope("request") and @Transactional(readOnly=true) through a
single annotation.
* Standardized dependency injection annotations:
Spring 3.0 comes with full support for the JSR-330 specification for
Dependency Injection in Java – annotation-driven injection via @Inject
and its associated qualifier and provider model, as an alternative to
Spring's own @Autowired and co.
* Declarative model validation based on constraint annotations:
Spring-style setup of a JSR-303 Bean Validation provider (such as
Hibernate Validator 4.0). Comes with an annotation-driven validation
option in Spring MVC, exposing a unified view on constraint violations
through Spring’s binding result facility.
* Enhanced binding and annotation-driven formatting:
Converter and Formatter SPIs as an alternative to standard
PropertyEditors. Formatting may be driven by annotations in a style
similar to JSR-303 constraints, e.g. using @DateTimeFormat. Also, check
out the new mvc namespace for convenient setup of formatting and
validation in Spring MVC.
* Comprehensive REST support: native REST
capabilities in Spring MVC, such as REST-style request mappings, URI
variable extraction through @PathVariable parameters, and view
resolution driven by content negotiation. Client-side REST support is
available in the form of a RestTemplate class.
* Rich native Portlet 2.0 support: Spring MVC fully
supports Portlet 2.0 environments and Portlet 2.0’s new event and
resource request model. Includes specialized mapping facilities for
typical portlet request characteristics: @ActionMapping,
@RenderMapping, @ResourceMapping, @EventMapping.
* Object/XML Mapping (OXM): as known from Spring
Web Services, now in Spring Framework core. Marshalling and
Unmarshaller abstractions with out-of-the-box support for JAXB 2,
Castor, etc. Comes with integration options for XML payloads in Spring
MVC and Spring JMS.
* Next-generation scheduling capabilities: new
TaskScheduler and Trigger mechanisms with first-class cron support.
Spring 3.0 comes with a convenient task namespace and also supports
@Async and @Scheduled annotations now. This can be executed on top of
native thread pools or server-managed thread pools.
Beyond those big themes, there are hundreds of refinements in the
details which you will particularly appreciate when upgrading from
Spring 2.5. Check the changelog and the javadocs…
In terms of system requirements, Spring 3.0 covers a broad range of
environments. For two key characteristics, Spring 3.0 supports Java SE 5 and above and Servlet 2.4 and above, e.g. Tomcat 5.x and 6.x, also retaining compatibility with common enterprise servers such as WebSphere 6.1 and WebLogic 9.2
(which are formally still based on J2EE 1.4). At the same time, we
support GlassFish v3 already – adapting to Java EE 6 API level in
Spring as well.
As a consequence, Spring 3 brings brand-new component model features, and also standards like JSR-330 injection and JSR-303 validation, to established production environments – without having to upgrade your server installation! All you have to do is to upgrade the application libraries of your Spring-powered application to Spring 3.0…
Enjoy – and watch out for follow-up posts about specific Spring 3 features, as well as for samples running on Spring 3.0!
struts2的文件上传对文件大小的限制,缺省值是2m,也就是说缺省情况下,最大只能上传2m的文件。根据文档所说需要对fileUpload这个拦截器的一个参数maximunSize进行设置
<interceptor-ref name="fileUpload">
<param name="maximumSize">1000000</param>
<param name="allowedTypes">image/gif,image/jpeg,image/jpg,image/png</param>
</interceptor-ref>
但是我设置了之后并没有作用。
后来,仔细查看日志后才发现错误是commons-fileupload里面的文件大小限制引起了错误。
在struts.xml中,添加
<constant name="struts.multipart.maxSize" value="16777216"/>
解决问题!
摘要: JavaRebel是一个工具,主要是用于热加载,比如说在Tomcat之类的应用服务器中,更新了class或者某些资源文件,使用了JRebel之后,就不需要重新启动应用服务器。这对于开发的人来说,是特别方便的。当然Java也提供了HotSpot的JVM,但是如果你修改的类中有方法名称变动的话,HotSpot就无能为力了,必须要重要启动应用服务器。
这里有一点先声明一下,本文只是破解仅限于学习和研究... 阅读全文
目前从实际应用来看,ORM的老大自然是Hibernate,可是iBatis因为相对比较直观、学习曲线相对较低,因而也赢得了不少用户的青睐。
本文主要介绍作为iBatis辅助工具的iBator的使用方法。
iBator是一个iBatis相关代码的自动生成工具。
1、安装iBator的插件
在Eclipse中,使用添加站点的方法,输入网址http://ibatis.apache.org/tools/ibator,进行iBator的安装。
2、建议不要直接在使用iBatis的项目里直接使用iBator,推荐另外单独建立一个项目来生成。比如,建立一个项目叫:IbatorPrj
3、右键点击IbatorPrj这个项目,如果刚才的插件安装正确的话,就会看到一个“Add iBATOR to the build path”的选项,点击一下。
4、创建iBator的配置文件。下面是我的例子,大家在实际使用的过程中,需要根据自己的情况进行相应的修改。
主要就是数据库JDBC库的路径、数据库驱动的类名、项目的名称、包名等。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ibatorConfiguration
PUBLIC "-//Apache Software Foundation//DTD Apache iBATIS Ibator Configuration 1.0//EN"
"http://ibatis.apache.org/dtd/ibator-config_1_0.dtd">
<ibatorConfiguration>
<classPathEntry location="c:\javaLibs\MySql\mysql-connector-java-5.0.6-bin.jar" />
<ibatorContext id="SampleiBator" targetRuntime="Ibatis2Java5">
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost/sample" userId="root" password="admin">
</jdbcConnection>
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<javaModelGenerator targetPackage="com.sample"
targetProject="IbatorPrj\src">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<sqlMapGenerator targetPackage="com.sample.xml"
targetProject="IbatorPrj\src">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<daoGenerator type="GENERIC-CI" targetPackage="com.sample.dao"
targetProject="IbatorPrj\src">
<property name="enableSubPackages" value="true" />
</daoGenerator>
<table schema="sample" tableName="tab1" domainObjectName="JavaBean1">
<property name="useActualColumnNames" value="false" />
<generatedKey column="ID" sqlStatement="MySql" identity="true" />
</table>
</ibatorContext>
</ibatorConfiguration>
5、配置文件生成完毕后,右键点击这个文件,选择“Generate iBatis Artifact”,然后你就在配置的文件夹下找到自动生成的文件了。
摘要: Tapestry IoC容器从历史上来说,是从从HiveMind继承发展而来,但是HiveMind和目前大红大紫的Spring都不能满足Tapestry的一些特定的需求,所以全新开发了一套IoC的容器。
其核心思想就是使用Java代码自身来解决依赖注入而不是由Xml之类的配置文件来完成,这和Guice的思想是非常相似的,Lewis也承认从Guice那里借鉴了不少。
另外需要说明一下的是,Tapesty还从中国的一个非常古老但又充满哲理的游戏--围棋中借鉴了一些术语和思想。大意是围棋中经常要把棋子走的轻盈(Lightness),让每个棋子都能尽量地高效。编程也一样要轻量(Lightness)。 阅读全文
在应用中一般普通的JavaPojo都是由Spring来管理的,所以使用autowire注解来进行注入不会产生问题,但是有两个东西是例外的,一个是Filter,一个是Servlet,这两样东西都是由Servlet容器来维护管理的,所以如果想和其他的Bean一样使用Autowire来注入的话,是需要做一些额外的功夫的。
对于Filter,Spring提供了DelegatingFilterProxy,所以本文主要讲述Servlet的解决。
1、比较直观但是不大优雅的做法是重写init()方法,在里面使用AutowireCapableBeanFactory来手工告诉Spring:我这个Servlet是需要这样的一个Bean的。具体写法:
public void init(ServletConfig servletConfig) throws ServletException {
ServletContext servletContext = servletConfig.getServletContext();
WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
AutowireCapableBeanFactory autowireCapableBeanFactory = webApplicationContext.getAutowireCapableBeanFactory();
autowireCapableBeanFactory.configureBean(this, BEAN_NAME);
}
其中,BEAN_NAME就是需要注入的Bean在spring中注册的名字.
这样写的主要问题是就是那个BEAN_NAME,这样写有点主动查找,而不是依赖注入的感觉。
2、创建一个类似于DelegatingFilterProxy那样的代理,通过代理根据配置来找到实际的Servlet,完成业务逻辑功能。
假定我们有一个Servlet名字叫UserServlet,需要注入一个UserManager,伪代码如下:
public class UserServlet extends HttpServlet {
@Autowired(required = true)
private UserManager userManager;
}
第一步:
public class DelegatingServletProxy extends GenericServlet {
private String targetBean;
private Servlet proxy;
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
proxy.service(req, res);
}
@Override
public void init() throws ServletException {
this.targetBean = getServletName();
getServletBean();
proxy.init(getServletConfig());
}
private void getServletBean() {
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
this.proxy = (Servlet) wac.getBean(targetBean);
}
}
第二步:
配置web.xml文件,原来UserServlet的配置大致是这样的:
<servlet>
<servlet-name>userServlet</servlet-name>
<servlet-class>com.sample.UserServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>userServlet</servlet-name>
<url-pattern>/userServlet</url-pattern>
</servlet-mapping>
现在修改为
<servlet>
<servlet-name>userServlet</servlet-name>
<servlet-class>com.sample.DelegatingServletProxy</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>userServlet</servlet-name>
<url-pattern>/userServlet</url-pattern>
</servlet-mapping>
注意,spring是根据Servlet的名字来查找被代理的Servlet的,所以,首先我们要在UserServlet类前面加上@Component,来告诉Srping:我也是一个Bean。如果名称和Web.xml里面定义的不一样的话,可以在这里指定Bean的名字,比如: @Component("userServlet")
在我的随笔 Extjs Tree + JSON + Struts2中我介绍了如何异步加载一个Extjs的树,但是很多网友留言说不能成功操作。现在我自己做了一个所有源代码的包,供大家下载。
有几点事项请大家注意
1、blogjava的文件上载要求单个文件不能超过4M,所以,我把web-inf目录下的所有jar文件删除了。
所有jar文件的列表是:
commons-beanutils-1.7.0.jar
commons-collections-3.2.jar
commons-digester-1.6.jar
commons-lang-2.3.jar
commons-logging-1.1.jar
dom4j-1.6.1.jar
ezmorph-1.0.4.jar
freemarker-2.3.8.jar
javassist-3.8.1.jar
json-lib-2.2.1-jdk15.jar
log4j-1.2.13.jar
ognl-2.6.11.jar
struts2-core-2.0.11.jar
xml-apis-1.0.b2.jar
xwork-2.0.4.jar
注意红色标记的那个jar文件是上次随笔中遗漏了的。这个文件是需要的。
2、blogjava要求上传文件不能是war文件,所以我把war文件改成了rar后缀。
文件的URL: war文件下载
|