#
Showing posts with label Java. Show all posts
Showing posts with label Java. Show all posts

Wednesday, April 25, 2012

Scala, JPA and annotations

I've been trying to get onto the Scala Bandwagon and have been diligently reading some books that cover core scala well. However, like every programmer I'm enthusiastic to apply my acquired black arts to my work and justify to myself that this truly is productive.

While trying to apply my skills immediately is admittedly been a bit clunky, I finally managed to get a JPA, Scala piece of work in place. So as usual lets dive into the code.

Note: I'm still a learner, so if anyone has a better approach or feels I'm wrong somewhere or have misunderstood/misinterpreted something, please feel free to shoot.

  1. Ensure your Dependencies are right. For this check your JPA & Scala dependencies. If you are self sufficient "good for you", if not; this is what I used :
        ...
          
            2.9.2
            ...
            5.1.18        
            2.0.0
            1.4        
            1.0.1.Final
            3.3.2.GA
            3.4.0.GA
            3.3.0.ga
            3.4.0.GA 
            0.9.1.2
          
        ...
          
            ...
     
            scala
            Scala Tools
            http://scala-tools.org/repo-releases/
            
                true
            
            
                false
            
        
          ...
          
    
      
        
            scala
            Scala Tools
            http://scala-tools.org/repo-releases/
            
                true
            
            
                false
            
        
      
            
         ....
        
      
      
       org.hibernate.javax.persistence
       hibernate-jpa-2.0-api
       ${hibernate.jpa2.version}
      
      
      
            org.hibernate
            hibernate-core
            ${hibernate.core}
            
        
         commons-collections
         commons-collections     
        
               
         
      
       org.hibernate
       hibernate-commons-annotations
       ${hibernate.commons.annotations.version}
       
        
         javax.persistence
         persistence-api     
           
        
         hibernate
         org.hibernate     
            
       
      
      
       org.hibernate
       hibernate-annotations
       ${hibernate.annotations.version}
        
        
      
       org.hibernate
       hibernate-entitymanager
       ${hibernate.em.version}
       
        
              
                net.sf.ehcache
                ehcache
                  
       
      
            
                mysql
                mysql-connector-java
                ${mysql.java.connector.version}
            
            
                org.hsqldb
                hsqldb
                ${hypersonic.db.version}
                test
            
            
      
       c3p0
       c3p0
       ${pooling.c3p0.version}
      
            
            
                javax.validation
                validation-api
                1.0.0.GA
                compile
            
    
     
     
      org.scala-lang
      scala-library
      ${scala.version}
     
        
       
  2. Setup your Entity manager and DB connection Pool. I hooked mine to MySQL. Am assuming Connection Pool details not required. Plenty of articles for that. I use Spring with org.springframework.orm.jpa.JpaTransactionManager, com.mchange.v2.c3p0.ComboPooledDataSource, org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.
    Note: My dependencies also rely on Spring, but I have omitted those in my POM config sample above for brevity as they are standard Spring dependencies.
  3. In the source folder : src/main/scala. I created two Files:
    package com.neurosys.quizapp.domain
    
    import javax.persistence.Entity
    import javax.persistence.OneToMany
    import javax.persistence.GeneratedValue
    import scala.annotation.target.field
    import javax.persistence.Id
    import javax.persistence.GenerationType
    import javax.persistence.CascadeType
    import javax.persistence.FetchType
    import javax.persistence.OneToOne
    
    /**
     * @author Arjun Dhar, NeuroSystems Technologies Pvt. Ltd.
     */
    @Entity(name="questions")
    case class Question(text: String) {   
        //def this() = this(null)
            
        @(Id @field)
        @(GeneratedValue @field)(strategy = GenerationType.AUTO)    
     var id:Long = 0L 
     def setId(id:Long) =  {this.id = id}
        def getId:Long = this.id    
        
        def getText = this.text
     
        @OneToOne(optional=false)
     var correctAnswer:Answer = null
     def setCorrectAnswer(correctAnswer:Answer) =  {this.correctAnswer = correctAnswer}
        def getCorrectAnswer:Answer = this.correctAnswer
    
        @OneToMany(fetch=FetchType.EAGER, mappedBy="question")
        var answers : java.util.List[Answer]= _
        def setAnswers(answers:java.util.List[Answer]) =  {this.answers = answers}
        def getAnswers:java.util.List[Answer] = this.answers   
    }
    

    and
    package com.neurosys.quizapp.domain
    import javax.persistence.Entity
    import javax.persistence.OneToOne
    import javax.persistence.GeneratedValue
    import scala.annotation.target.field
    import javax.persistence.Id
    import javax.persistence.GenerationType
    import javax.persistence.ManyToOne
    
    /**
     * @author Arjun Dhar, NeuroSystems Technologies Pvt. Ltd.
     */
    @Entity(name="answers")
    case class Answer(text:String, sequence:Int) {
        //def this() = this(null, "", 0)
        def this(question:Question, text:String, order:Int) = {
          this(text, order);
          this.question = question;
        }
      
        @(Id @field)
        @(GeneratedValue @field)(strategy = GenerationType.AUTO)    
     var id:Long = 0L
     def setId(id:Long) =  {this.id = id}
        def getId:Long = this.id 
        
        @OneToOne(optional=false)
        var question:Question = null
        //def setQuestion(question:Question) = this.question = question
        def getQuestion = this.question
        
        //var text:String = null
        //def setText(text:String) = this.text = text
        def getText = this.text
        
        def getSequence = this.sequence;
    }
    
  4. What does the above do?

    I wanted to create a simple question bank as a sample exercise.

    I created a model where one Question can have only one correct answer which the question object is aware of. Also, the answers know which question they belong to.
    Interesting points to note:
    • In Scala, though you can define your members within the constructor arguments itself, Annotating them there does not work -- took a lot of my time. Frankly a bit annoyed it did not work, but I did expect magic!
      Example: case class Answer(@OneToOne question:Question, text:String, sequence:Int) will not work.
    • Notice the style@(GeneratedValue @field)
    • I adopted the use of Scala case class; helps in scala taking care of the euqals and hashCode methods which from an OR framework perspective deserves attention.
    • You do not have to have your Scala Entity look like a total Java Bean, have the constructor do as much of the hard work and simply implement getters (leave the setters / optional)
    • In the Questions entity, the last field called answers has to be a traditional Java Collection. i.e. You cannot use the Scala Trait Seq or List. I would be interested to see the JPA flavour for Scala support these things.
...voila, it worked!

Tuesday, July 19, 2011

Spring 3.1 & EhCache Integration

Context: How to integrate EhCache via Spring 3.1 (At the time of Writing Spring ver:3.1.0.M2)


1. Setup your library dependencies correctly

PSEUDO CONFIG : Maven config for reference and not to be copied as is :
  
    3.1.0.M2
  
  
  
 
 
  spring-maven-release
  Spring Maven Release Repository
  http://maven.springframework.org/release
 
 
  spring-maven-milestone
  Spring Maven Milestone Repository
  http://maven.springframework.org/milestone
 

      ehcache-repo
      EhCache Repository
   https://oss.sonatype.org/content/repositories/sourceforge-releases
      
          false
      
      
         true
      
     
 
    
...

    
      
    

    
      net.sf.ehcache 
      ehcache-core
      2.4.3
    


NOTE: An important point to note is that many a time an Old Spring Library or worse, a conflicting EhCache Library (from say a Hibernate) dependency will give errors. The errors will looks like "Spring <init> method failures". To avoid this, please do a mvn dependency:tree on your modules and do any <exclusions> to other versions of EhCache.


2. Define You interface (Method Caching) : We are effectively ensuring all method operations are cached to cache publishContent. Furthermore, the identifier key is based on the URI.getLocation(). The cache can be applied on the interface or an Impl. You can write a default implementation for your interface and wire it via Spring (@Component)



For brevity, we will skip details of Method Caching. There are several articles on it. Note the use of SpEL. Can be also used for doing conditional caching. For example, in the sample interface below we are interested in caching only Content that is PUBLISHED.
/**
 * An adapter to extract the content from a specified location. Piped with {@link ContentLocationResolver} this
 * can locate and extract content from any Data Source.
 * 
 * @author Arjun Dhar
 *
 */
public interface ContentExtractor { 
 /**
  * Get Content. PUBLISHED Content is cached
  * 
  * @see {@link Cacheable}
  */
 @Cacheable(key="#location.path", 
      condition="(#location!=null and #context!=null and " +
          "T(com.neurosys.pms.content.domain.Status).PUBLISH == #context.status)" , value="publishContent") {
 public String getContent(URI location, EditableContentContext context);
}


Important Note:
From http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/cache.html In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual caching at runtime even if the invoked method is marked with @Cacheable - considering using the aspectj mode in this case.


3. Define Spring Binding/Config for EhCache

  xmlns:p="http://www.springframework.org/schema/p"
  xmlns:cache="http://www.springframework.org/schema/cache"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">

    

  
         p:config-location="classpath:com/neurosys/pms/modules/content/ehcache.xml"
      p:shared="false"
      >
     
      
     

  


4. Define the Caching configs in ehcache.xml
             xsi:noNamespaceSchemaLocation="ehcache.xsd"
             updateCheck="false" monitoring="autodetect"
             dynamicConfig="true">

  
  
  
    
  
                                
  


5. Sanity Unit test to ensure all the bindings etc are correctly setup
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={
  "classpath:*applicationContext.xml" //Assumes the Spring config is in or imported via your applicationContent.xml
 }) 
public class ContentLocateFetchTest {
 @Autowired
 private ContentExtractor contentExtractor;
 
 @Test
 public void testCache() {
  //No context status, hence it will fetch the data twice. One for each call.
  //If you put a Debug Statement in contentExtractor.getContent(), you will see it called twice for google.com
  contentExtractor.getContent(new URI("http://google.com"), null);
  contentExtractor.getContent(new URI("http://google.com"), null);
  
  EditableContentContext cntx = new EditableContentContext() {
   @Override public Authorization getAuthorization() {return null;}
   @Override public Status getStatus() {return Status.PUBLISH;}   
  };
  
  //If you put a Debug Statement in contentExtractor.getContent(), you will see it called only once for yahoo.com
  //since it detects that the context has a Status=PUBLISH and caches it.
  contentExtractor.getContent(new URI("http://yahoo.com"), cntx);
  contentExtractor.getContent(new URI("http://yahoo.com"), cntx);  
 }
}


You may also want to programatically refer to the cache to refresh or clear it etc:
 @Autowired
 @Qualifier("cacheManageName")
 private EhCacheCacheManager cacheManager; .... 
...
Cache cache = cacheManager.getCache("cahceName");
    cache.clear();
..In the above the cache name is that is supplied in <cache name="chacheName"...

- Arjun Dhar
My Company NeuroSys

Tuesday, January 11, 2011

Setter Chaining & generics

Although the concept of setter chaining is not new, thanks to a few good men I've picked the habit. However when using Inheritance, the setter of the base class returns type of "base class". This can disrupt the "coolness" of using chaining.

For the rest I'll let the code talk about the issue & a possible solution in more detail:


package com.arjun.misc;

/**
* "Setter chaining". The issue is that it returns the object of the
* type of the class in which the setter is.
* For Inheritance concepts this means if A<--B; then B.a()
* will return A not B (exclude overriding).
* This hardly helps chaining. Using generics to solve the problem
*
* @author Arjun Dhar | www.neurosys.biz
* */

public class GenericChainingSetters {
private static class A<T extends A> {
T setA(String something) {
return (T)this;
}
}

private static class B<T extends B> extends A<T> {
T setB(String something) {
return (T)this;
}
}

private static class C<T extends C> extends B<T> {
T setC(String something) {
return (T)this;
}
}

public static void main(String[] args) {
//WORKS
B<B> b = new B();
b.setA("abc").setB("abc again?!");

//WORKS
C<C> c = new C();
c.setA("abc").setB("abc again?!").setC("Hurray!");
}
}

Only thing is that as with Generics (erasure),
one cant (can but should not logically) do something stupid like:
B<C> b = new B();


Thoughts & suggestions welcome

thanks
-Arjun

Click here to see better way using Java 8