所有收到的問題都是新的功能要求。
所以,如果沒有意外的話,Beta10將會用于投票表決是否作為GA(General Available)版本。
posted @ 2010-03-08 19:50 云自無心水自閑 閱讀(1757) | 評論 (1) | 編輯 收藏
|
|||
2月25日,IBatis3發(fā)布了Beta10,在主頁上宣稱,一個月來Beta9的公測都沒有收到真正的Bug提交。
所有收到的問題都是新的功能要求。 所以,如果沒有意外的話,Beta10將會用于投票表決是否作為GA(General Available)版本。 posted @ 2010-03-08 19:50 云自無心水自閑 閱讀(1757) | 評論 (1) | 編輯 收藏 在教程的第四部分(http://www.aygfsteel.com/usherlight/archive/2009/06/20/283396.html)我們已經(jīng)粗略地介紹了Tapestry的前綴,其中提及最常用的兩種literal和prop。這里順便再介紹一下其他的幾個前綴. 自定義prefix一般來說是3個步驟, 看一下具體的prefix的類: 1
![]() 2 ![]() 3 ![]() 4 ![]() 5 ![]() 6 ![]() 7 ![]() 8 ![]() 9 ![]() 10 ![]() 11 ![]() 12 ![]() 13 ![]() 14 ![]() 15 ![]() 16 ![]() 17 ![]() 18 ![]() 19 ![]() 20 ![]() 21 ![]() 22 ![]() 23 ![]() 24 ![]() 25 ![]() 26 ![]() 27 ![]() 28 ![]() 29 ![]() 30 ![]() 31 ![]() 32 ![]() 33 ![]() 34 ![]() 35 ![]() 36 ![]() 37 ![]() 38 ![]() 39 ![]() 40 ![]() 41 ![]() 42 ![]() 1
![]() 2 ![]() 3 ![]() 4 ![]() 5 ![]() 6 ![]() 7 ![]() 8 ![]() 9 ![]() 10 ![]() 11 ![]() 12 ![]() 13 ![]() 14 ![]() 15 ![]() 16 ![]() 17 ![]() 18 ![]() 19 ![]() 20 ![]() 21 ![]() 22 ![]() 23 ![]() 24 ![]() 25 ![]() 26 ![]() 27 ![]() 28 ![]() 29 ![]() 30 ![]() 31 ![]() 32 ![]() 33 ![]() 34 ![]() 35 ![]() 36 ![]() 37 ![]() 38 ![]() 39 ![]() 40 ![]() 41 ![]() Binding和BindingFactory寫好了,注冊后就可以使用了,注冊的過程是在AppModel中添加以下一段代碼: 1
![]() 2 ![]() 3 ![]() 4 ![]() 5 ![]() 6 ![]() 7 ![]() posted @ 2010-03-06 15:54 云自無心水自閑 閱讀(2255) | 評論 (0) | 編輯 收藏 1. 先開始看SpringSide吧。 posted @ 2010-03-01 12:24 云自無心水自閑 閱讀(405) | 評論 (0) | 編輯 收藏 一直覺得EJB好像已經(jīng)日薄西山了,但是實際上生命力還挺頑強的。 posted @ 2010-03-01 12:21 云自無心水自閑 閱讀(387) | 評論 (0) | 編輯 收藏 我記得好像是Appfuse的作者曾經(jīng)這樣評價過Tapestry:只要你真正掌握了Tapestry,你的開發(fā)效率將會得到極大的提高。為什么呢?我認為他這樣說的一個重要原因就是Tapestry的組件機制。Tapestry提供了非常便利的組件定義機制,隨著Tapestry的組件不斷積累,Tapestry的開發(fā)將會變得越來越簡單。
本文就用一個實例來看一下Tapestry中是如何添加一個自定義組件的。 Tapestry的內(nèi)置組件只提供了checkbox,而且只能返回一個boolean,用于表明是否被選中。 比如,要進行一個群眾喜愛的水果調(diào)查,選項有: 蘋果,葡萄,桃子,香蕉...,就需要對應每個選項設置一個布爾型變量,顯得比較繁瑣。 這里我們將添加一個組件用于將一組checkbox集中起來返回一個逗號分隔的字符串值。 通過查看Tapestry中的checkbox的源碼(已經(jīng)附在文章的后面)可以知道,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; posted @ 2010-02-12 07:26 云自無心水自閑 閱讀(3311) | 評論 (3) | 編輯 收藏 在tapestry5中,在頁面之間傳遞基本有3種方法 @Property @Property Object onFormSubmit() { private List<String> paramList; 在Input頁面中,需要把onFormSubmit改一下: 其中,需要注意的是output中的onActivate方法,基參數(shù)的順序必須和List中放入的參數(shù)順序一致。
posted @ 2010-02-06 12:36 云自無心水自閑 閱讀(3586) | 評論 (4) | 編輯 收藏 在上一篇中我們研究了如何實現(xiàn)SpringSecurity中Jsp Tag的<security:authorize ifAllGranted="ROLE_SUPERVISOR">的功能。這一次我們一起研究一下如何實現(xiàn)在Tapestry5.1中添加一個Filter來對所有的操作進行權限的過濾控制。 這就是Filter的代碼,這個Filter必須實現(xiàn)ComponentRequestFilter接口。值得注意的是其構(gòu)造函數(shù)所需要用到的4個參數(shù),這4個參數(shù)都是Tapestry5本身自有的服務,所以我們什么也不用做,Tapestry5自動會將服務的實例注入進來,這就是Tapestry-IOC的威力。 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來取得。 現(xiàn)在我們需要把剛定義的Filter注冊到系統(tǒng)中,很簡單,只要在AppModule中添加以下函數(shù)就行了: 1 public static void contributeComponentRequestHandler(
從本例子中我們可以看到Tapesty Ioc容器使用的便利性,也認識到了Ioc容器在Tapestry體系中的重要性2 OrderedConfiguration<ComponentRequestFilter> configuration) { 3 configuration.addInstance("RequiresLogin", RequiresLoginFilter.class); 4 } 5 posted @ 2010-02-04 19:17 云自無心水自閑 閱讀(3230) | 評論 (1) | 編輯 收藏 IBatis2中提供了3種DataSource的配置:JNDI, Apache DBCP, IBatis自帶的SimpleDataSource。但在IBatis3中只提供了兩種DataSource: UNPOOLED, POOLED。
如果要實現(xiàn)自定義的DataSource,就需要通過擴展DataSourceFactory。本文就演示一下這個過程。 準備工作:Connection Pool的選擇,通過搜索發(fā)現(xiàn)目前比較流行的免費數(shù)據(jù)庫連接池主要有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個項目都已經(jīng)挺長時間沒有更新了。但是總體評價上C3P0無論從穩(wěn)定上還是效率上都要好一點。 (具體這3個項目誰更優(yōu)秀,并不是本文的重點,本文主要是介紹一下如何在IBatis3中自定義數(shù)據(jù)源) 大致步驟: 1、實現(xiàn)org.apache.ibatis.datasource.DataSourceFactory接口,主要是2個方法 a、public DataSource getDataSource() 如何具體地得到一個數(shù)據(jù)源 b、public void setProperties(Properties properties) 如何設置數(shù)據(jù)源的參數(shù)屬性 2、實現(xiàn)javax.sql.DataSource,這個就是提供給DataSourceFactory的實例 3、在IBatis3中引用新加入的數(shù)據(jù)源 1. 從代碼中可以看出,IBatis3與IBatis2不同,不再通過一個Configuration類來進行數(shù)據(jù)源屬性的設置,而是使用反射機制直接調(diào)用數(shù)據(jù)源的方法來設置參數(shù)。 這就要求配置文件中的參數(shù)名稱必須與數(shù)據(jù)源類中的方法名匹配. 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. 數(shù)據(jù)源類,其中的一堆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中,可以先定義數(shù)據(jù)源的別稱,然后就象POOLED和UNPOOLED一樣使用別稱來引用數(shù)據(jù)源。 <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> posted @ 2010-02-01 12:57 云自無心水自閑 閱讀(3921) | 評論 (2) | 編輯 收藏 Tapestry5.1中java.lang.ClassFormatError: Invalid length nnn in LocalVariableTable in class file 錯誤的解決最近在使用tapestry5.1.0.5開發(fā)項目的時候,突然報錯:
Exception in thread "main" java.lang.ClassFormatError: Invalid length 561 in LocalVariableTable in class file 在網(wǎng)上搜索后,發(fā)現(xiàn)有人也有同樣的錯誤,解決方法有兩種: 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文件時報的錯誤, 我去網(wǎng)上搜索這方面的信息,發(fā)現(xiàn)有好幾個人都和我一樣在使用javassists3.11.0GA版本的時候,會出現(xiàn)這個錯誤。 后來,我改用Tapestry5中自帶的javassists3.9.0GA后,問題消失了。 這次經(jīng)驗教訓是并不是所有最新的東西都是最好的。合適的才是最好的。 posted @ 2010-01-21 07:28 云自無心水自閑 閱讀(3684) | 評論 (0) | 編輯 收藏 IBatis3的Beta8版本已經(jīng)發(fā)布了,在官方網(wǎng)站上聲稱目前的版本已經(jīng)非常穩(wěn)定,只有4個已知的問題,其中2個是非功能性的。作者宣稱,這樣的狀況使它對于近期發(fā)布GA版本充滿信心。
那么IBatis3與IBatis2相比,究竟變化在哪里呢? 最重要的變化是IBatis3中引入了接口綁定(Interface Binding)的概念。在IBatis2中,沒有應用Java5的泛型,所以需要大量使用強制類型轉(zhuǎn)換,比如: 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是一個自定義的接口(注意,開發(fā)人員只需要定義一個接口,不需要提供具體的實現(xiàn)) public interface EmployeeMapper { Employee getEmployee (int employeeId); List<Employee> listAllEmployees(); } 這樣就行了,IBatis會自動為你生成接口的具體實現(xiàn)。是不是感覺有點酷? posted @ 2010-01-15 20:49 云自無心水自閑 閱讀(2372) | 評論 (4) | 編輯 收藏 |
|||