[Logo] Terracotta Discussion Forums (LEGACY READ-ONLY ARCHIVE)
  [Search] Search   [Recent Topics] Recent Topics   [Members]  Member Listing   [Groups] Back to home page 
[Register] Register / 
[Login] Login 
[Expert]
Spring Annotated Controllers  XML
Forum Index -> Terracotta for Spring
Author Message
stuartw

neo

Joined: 12/23/2009 20:48:56
Messages: 7
Offline

I am using Terracotta 3.1.1 for session clustering and hibernate caching. However when I try to use a Spring annotated controller I am getting an exception:

javax.servlet.ServletException: No adapter for handler [au.domain.controller.UserController@1c7e64c]: Does your handler implement a supported interface like Controller?

This only occurs once I run the application with Terracotta, it does not occur if the app runs without Terracotta... hence the post here and not on the Spring forum.

I have read through the documentation for clustering a Spring app but nothing there seems to allude to my problem.

Is there something I need to put into my tc-config.xml file to enable Spring annotated controllers to work?

I can supply any config and code if required.


steve

ophanim

Joined: 05/24/2006 14:22:53
Messages: 619
Offline

Would be great if you could supply the code.

Want to post to this forum? Join the Terracotta Community
stuartw

neo

Joined: 12/23/2009 20:48:56
Messages: 7
Offline

Here is the relevant code

tc-config.xml
Code:
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
 
 All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved.
 
 
 
 -->
 <!-- This is a Terracotta configuration file that has been pre-configured
 for use with Tomcat.
 
 For more information, please see the product documentation.
  -->
 <tc:tc-config xmlns:tc="http://www.terracotta.org/config"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.terracotta.org/schema/terracotta-5.xsd">
 
   <!-- Tell DSO where the Terracotta server can be found -->
   <servers>
     <server host="localhost">
       <data>%(user.home)/terracotta/server-data</data>
       <logs>%(user.home)/terracotta/server-logs</logs>
     </server>
   </servers>
 
   <!-- Tell DSO where to put the generated client logs -->
   <clients>
     <logs>%(user.home)/terracotta/client-logs</logs>
     <modules>
       <!-- Include the Terracotta Integration Module for the appropriate version of 
       your sessions container.  You can view the list of supported TIMs with the 
       tim-get tool in the bin directory:
         bin/tim-get.sh list
       The TIM version will vary depending on the Terracotta release.  To get
       the correct version, use the tim-get tool in the bin directory to download the 
       latest appropriate version with a command like:
          bin/tim-get.sh install tim-tomcat-6.0
       -->
       <module name="tim-tomcat-6.0" />
 	  <module name="tim-hibernate-cache-3.3" version="1.0.1" />
 	  <module name="tim-spring-security-2.0" />
     </modules>
   </clients>
 
   <application>
     <dso>
       <!-- The following declarations tells DSO which classes should be instrumented to 
       allow sharing. When the app runs under DSO, shared instances of these classes will
       broadcast changes in their state.
 
       A good idiom when writing an app that you intend to cluster via TC DSO is to group the 
       classes you wish to share under a single package (although if you follow the MVC pattern
       this tends to happen naturally) - this way the list of classes you wish to instrument
       can be concise -->
       <instrumented-classes>
         <!-- Start by including just the classes you expect to get added to the shared 
         graph.  These typically include domain classes and shared data structures.  If you 
         miss classes, Terracotta will throw NonPortableOjectExceptions telling you more 
         about what needs to be added. -->
         <include>
           <class-expression>au.domain..*</class-expression>
         </include>
       </instrumented-classes>
 
       <!-- Declare which web application context names should use DSO sessions -->
       <web-applications>
 	  
         <web-application>WebClusterTest</web-application>
       </web-applications>
     </dso>
   </application>
 </tc:tc-config>
 
 


applicationContext-web.xml
Code:
 <?xml version="1.0" encoding="UTF-8"?>
 <beans
     xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:p="http://www.springframework.org/schema/p"
     xmlns:aop="http://www.springframework.org/schema/aop"
     xmlns:context="http://www.springframework.org/schema/context"
     xmlns:jee="http://www.springframework.org/schema/jee"
     xmlns:tx="http://www.springframework.org/schema/tx"
     xmlns:security="http://www.springframework.org/schema/security"
     xsi:schemaLocation="
 			http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
 			http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
 			http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
 			http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd
 			http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
             http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd">
 
     <security:http auto-config="true"> 
         <security:intercept-url pattern="/**" access="ROLE_USER" />
     </security:http>
     
     <security:authentication-provider>
         <security:user-service id="userDetailsService">
             <security:user name="user" password="password" authorities="ROLE_USER, ROLE_ADMIN" />
         </security:user-service>
     </security:authentication-provider>
     
 	<context:annotation-config />
 
 	<context:component-scan base-package="au.domain.controller"/>
 	
 	<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>
 
     <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
 
 	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" 
 		  p:prefix="/WEB-INF/pages/"
 	      p:suffix=".jsp"/>
     
     <aop:aspectj-autoproxy >
         <aop:include name="performanceMonitorAspect"/>
     </aop:aspectj-autoproxy>
 
 </beans>
 


applicationContext-services.xml
Code:
 <?xml version="1.0" encoding="UTF-8"?>
 <beans
     xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:p="http://www.springframework.org/schema/p"
     xmlns:aop="http://www.springframework.org/schema/aop"
     xmlns:context="http://www.springframework.org/schema/context"
     xmlns:jee="http://www.springframework.org/schema/jee"
     xmlns:tx="http://www.springframework.org/schema/tx"
     xsi:schemaLocation="
 			http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
 			http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
 			http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
 			http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd
 			http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
 
     <context:annotation-config />
 
     <bean id="dataSourceTarget"
           class="com.mchange.v2.c3p0.ComboPooledDataSource"
           destroy-method="close"
           p:driverClass="com.mysql.jdbc.Driver"
           p:jdbcUrl="jdbc:mysql://localhost:3306/test"
           p:user="root"
           p:password="password" />
 
     <bean id="dataSource"
           class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy"
           p:targetDataSource-ref="dataSourceTarget" />
 
     <bean id="sessionFactory"
           class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"
           p:dataSource-ref="dataSource"
           p:annotatedClasses="au.domain.model.User">
         <property name="hibernateProperties">
             <props>
                 <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
                 <prop key="hibernate.show_sql">true</prop>
                 <prop key="hibernate.format_sql">true</prop>
                 <prop key="hibernate.hbm2ddl.auto">update</prop>
                 <prop key="hibernate.cache.use_second_level_cache">true</prop>
                 <prop key="hibernate.cache.use_query_cache">true</prop>
                 <prop key="hibernate.cache.provider_class">org.terracotta.hibernate.TerracottaHibernateCacheProvider</prop>
                 <prop key="hibernate.generate_statistics">true</prop>
             </props>
         </property>
         <property name="eventListeners">
             <map>
                 <entry key="merge">
                     <bean class="org.springframework.orm.hibernate3.support.IdTransferringMergeEventListener" />
                 </entry>
             </map>
         </property>
     </bean>
 
     <bean id="transactionManager"
           class="org.springframework.orm.hibernate3.HibernateTransactionManager"
           p:sessionFactory-ref="sessionFactory" />
 
     <tx:annotation-driven />
 
     <aop:aspectj-autoproxy />
 
     <bean id="performanceMonitorAspect"
           class="au.domain.aspects.PerformanceMonitorAspect" />
 
     <bean id="actorService"
           class="au.domain.service.ActorServiceImpl" />
 
     <bean id="actorFacade"
           class="au.domain.facade.HibernateActorFacade" />
 </beans>
 
 



UserController.java
Code:
 package au.domain.controller;
 
 import java.util.Collection;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.ModelMap;
 import org.springframework.validation.BindingResult;
 import org.springframework.web.bind.annotation.ModelAttribute;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMethod;
 import org.springframework.web.bind.support.SessionStatus;
 
 import au.domain.model.User;
 import au.domain.service.ActorService;
 
 @Controller()
 @RequestMapping("/users.html")
 //@SessionAttributes("user")
 public class UserController {
 
 	ActorService actorService;
 	
 	/* (non-Javadoc)
      * @see au.domain.controller.UserManager#setActorService(au.domain.service.ActorService)
      */
 	@Autowired
 	public void setActorService(ActorService actorService) {
 		this.actorService = actorService;
 	}
 	
 	/* (non-Javadoc)
      * @see au.domain.controller.UserManager#populateUsers()
      */
 	@ModelAttribute("users")
     public Collection<User> populateUsers() {
         return this.actorService.getUsers();
     }
 	
 	/* (non-Javadoc)
      * @see au.domain.controller.UserManager#setupForm(org.springframework.ui.ModelMap)
      */
 	@RequestMapping(method = RequestMethod.GET)
     public String setupForm( ModelMap model) {
         User user = new User();
         model.addAttribute("user", user);
         return "users";
     }
 
     /* (non-Javadoc)
      * @see au.domain.controller.UserManager#processSubmit(au.domain.model.User, org.springframework.validation.BindingResult, org.springframework.web.bind.support.SessionStatus)
      */
     @RequestMapping(method = RequestMethod.POST)
     public String processSubmit(
             @ModelAttribute("user") User user, BindingResult result, SessionStatus status) {
     	status.setComplete();
         this.actorService.createUser(user);
         return "redirect:users.html";
     }
 
 }
 



Let me know if you need to look at anything else.

Thanks.

stuartw

neo

Joined: 12/23/2009 20:48:56
Messages: 7
Offline

This is the full stack trace:
Code:
 05/01/2010 9:49:56 AM org.apache.catalina.core.StandardWrapperValve invoke
 SEVERE: Servlet.service() for servlet Spring MVC Dispatcher Servlet threw exception
 javax.servlet.ServletException: No adapter for handler [au.domain.controller.UserController@10b226b]: Does your handler implement a supported interface like Controller?
 	at org.springframework.web.servlet.DispatcherServlet.getHandlerAdapter(DispatcherServlet.java:1100)
 	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:874)
 	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:807)
 	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:571)
 	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:501)
 	at javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
 	at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
 	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
 	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
 	at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:378)
 	at org.springframework.security.intercept.web.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:109)
 	at org.springframework.security.intercept.web.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83)
 	at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
 	at org.springframework.security.ui.SessionFixationProtectionFilter.doFilterHttp(SessionFixationProtectionFilter.java:67)
 	at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
 	at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
 	at org.springframework.security.ui.ExceptionTranslationFilter.doFilterHttp(ExceptionTranslationFilter.java:101)
 	at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
 	at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
 	at org.springframework.security.providers.anonymous.AnonymousProcessingFilter.doFilterHttp(AnonymousProcessingFilter.java:105)
 	at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
 	at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
 	at org.springframework.security.ui.rememberme.RememberMeProcessingFilter.doFilterHttp(RememberMeProcessingFilter.java:116)
 	at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
 	at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
 	at org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter.doFilterHttp(SecurityContextHolderAwareRequestFilter.java:91)
 	at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
 	at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
 	at org.springframework.security.ui.basicauth.BasicProcessingFilter.doFilterHttp(BasicProcessingFilter.java:174)
 	at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
 	at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
 	at org.springframework.security.ui.webapp.DefaultLoginPageGeneratingFilter.doFilterHttp(DefaultLoginPageGeneratingFilter.java:86)
 	at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
 	at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
 	at org.springframework.security.ui.AbstractProcessingFilter.doFilterHttp(AbstractProcessingFilter.java:277)
 	at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
 	at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
 	at org.springframework.security.ui.logout.LogoutFilter.doFilterHttp(LogoutFilter.java:89)
 	at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
 	at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
 	at org.springframework.security.context.HttpSessionContextIntegrationFilter.doFilterHttp(HttpSessionContextIntegrationFilter.java:235)
 	at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
 	at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
 	at org.springframework.security.util.FilterChainProxy.doFilter(FilterChainProxy.java:175)
 	at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:236)
 	at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:167)
 	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
 	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
 	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
 	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
 	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
 	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
 	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
 	at org.terracotta.modules.tomcat.tomcat_5_5.SessionValve55.tcInvoke(SessionValve55.java:68)
 	at org.terracotta.modules.tomcat.tomcat_5_5.SessionValve55.invoke(SessionValve55.java:55)
 	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
 	at org.apache.jk.server.JkCoyoteHandler.invoke(JkCoyoteHandler.java:190)
 	at org.apache.jk.common.HandlerRequest.invoke(HandlerRequest.java:291)
 	at org.apache.jk.common.ChannelSocket.invoke(ChannelSocket.java:769)
 	at org.apache.jk.common.ChannelSocket.processConnection(ChannelSocket.java:698)
 	at org.apache.jk.common.ChannelSocket$SocketConnection.runIt(ChannelSocket.java:891)
 	at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:690)
 	at java.lang.Thread.run(Thread.java:619)
 
teck

seraphim
[Avatar]
Joined: 05/24/2006 15:03:25
Messages: 1128
Offline

One thing that could help here would be to see this class after it has instrumented by terracotta. It "feels" like somehow the annotation are going missing after we manipulate it.

If you can add -Dtc.classloader.writeToDisk=true to your java startup (presumably in your tomcat start script). That setting should produce a lot of logging on the console saying "Writing resource...". For every type that terracotta instruments a file will be written in ${user.home}/adapated. The version of UserController that ends up there is what I'd like to have a look at.

thanks!

Tim Eck (terracotta engineer)
stuartw

neo

Joined: 12/23/2009 20:48:56
Messages: 7
Offline

No worries I have attached it here.

Cheers.
 Filename UserController.class [Disk] Download
 Description UserController adapted class
 Filesize 4 Kbytes
 Downloaded:  304 time(s)

teck

seraphim
[Avatar]
Joined: 05/24/2006 15:03:25
Messages: 1128
Offline

Thanks for the instrumented class. Not that I know what spring is looking for exactly but the annotations and interfaces present in the original class are still present in the version after we instrument it.

You've provided the code and config files, but a complete runnable example that reproduces the issue would be even better :-). Is that too big of a request?


Tim Eck (terracotta engineer)
stuartw

neo

Joined: 12/23/2009 20:48:56
Messages: 7
Offline

No that's fine the application is only a proof of concept. I have attached the war file here.

It should run on a default instance of Tomcat with a default install of MySQL.
I have added these lines to the tomcat startup.bat file:
Code:
 set TC_INSTALL_DIR="C:\Program Files\terracotta\terracotta-3.1.1"
 set TC_CONFIG_PATH=localhost:9510
 call %TC_INSTALL_DIR%\bin\dso-env.bat -q
 set JAVA_OPTS=%TC_JAVA_OPTS% -Dtc.classloader.writeToDisk=true
 

I have it running on two instances(v6.0.20) configured to run load balanced under Apache with mod_jk.

If I remove the above lines the users pages functions normally, otherwise I get the exception first mentioned.

Let me know if I have missed anything, or you need anything else.
 Filename WebClusterTest.war [Disk] Download
 Description Complete deployable war file.
 Filesize 12506 Kbytes
 Downloaded:  187 time(s)

teck

seraphim
[Avatar]
Joined: 05/24/2006 15:03:25
Messages: 1128
Offline

I can run the app, but I'm not entirely sure how to get something in the database so I can login. Bringing up the login page with and without Terracotta seems to work okay.

I made a quick attempt to insert some rows in the DB by hand but that didn't seem to work.


Tim Eck (terracotta engineer)
stuartw

neo

Joined: 12/23/2009 20:48:56
Messages: 7
Offline

The only credentials that work are:
username: user
password: password

At present the authentication is handled by Spring Security's AuthenticationProvider configured in the file WebContent/WEB-INF/config/applicationContext-web.xml:
Code:
 
     <security:http auto-config="true"> 
         <security:intercept-url pattern="/**" access="ROLE_USER" />
     </security:http>
     
     <security:authentication-provider>
         <security:user-service id="userDetailsService">
             <security:user name="user" password="password" authorities="ROLE_USER, ROLE_ADMIN" />
         </security:user-service>
     </security:authentication-provider>
 

This setup means that only the users listed in that file can authenticate, it does not authenticate against the database stored users.

Sorry, I should have mentioned this before.

Once you have logged in click on the users link(localhost:8080/WebClusterTest/users.html), that is the page that uses the annotated UserController class which seems to fail when Terracotta is used.
teck

seraphim
[Avatar]
Joined: 05/24/2006 15:03:25
Messages: 1128
Offline

Okee doke, I've managed to get the exception now.

I don't really claim to know what is really going wrong, but the terracotta instrumentation is adding some interfaces to UserController that seem to be interfering with the way spring wants to add advice into that class.

With terracotta it seems to be using a JDK based proxy and without terracotta it is using a CGLIB proxy. I traced things back to the code in spring's DefaultAopProxyFactory.createAopProxy():
http://springframework.cvs.sourceforge.net/viewvc/springframework/spring/src/org/springframework/aop/framework/DefaultAopProxyFactory.java?revision=1.19&view=markup.

The interfaces added by Terracotta make the last condition in there (hasNoUserSuppliedProxyInterfaces(config)) false.

Maybe this is enough information to help you debug this and maybe come up with a workaround?

One thing to note is that you can prevent Terracotta from instrumenting this class pretty easily. I added this to the tc-config.xml (after the include for au.domain..*) and things seemed to work okay:Code:
 <exclude>au.domain.controller.UserController</exclude>
 


Of course exluding this class from instrumentation means that instances of that type cannot be clustered. In general you want to keep the types included for terracotta instrumentation as tight/narrow as possible.




Tim Eck (terracotta engineer)
stuartw

neo

Joined: 12/23/2009 20:48:56
Messages: 7
Offline

Hi,

Thank you that is great information. There is no reason for the UserController class to be instrumented. It was pure laziness that the entry for instrumented classes in the tc-config.xml was so broad. Excluding that class does indeed resolve the problem.

Having said that however your investigation over which AOP proxy was being used made me look at the PerformanceMonitorAspect class. The around advice for logTime(..) was using the pointcut definition "within(au.domain..*)" (again lazily broad) which forced Spring to use the CGLIB proxies as it needed to proxy classes (such as UserController) as well as interfaces (see http://static.springsource.org/spring/docs/2.5.x/reference/aop.html#aop-introduction-proxies). Limiting the matching join points to interfaces meant that the UserController class can be instrumented by Terracotta and still used by Spring.

Thanks very much for your help!
teck

seraphim
[Avatar]
Joined: 05/24/2006 15:03:25
Messages: 1128
Offline

good deal. glad I could help

Tim Eck (terracotta engineer)
 
Forum Index -> Terracotta for Spring
Go to:   
Powered by JForum 2.1.7 © JForum Team