============
web.clj
============
(ns com.myapp.service.web
(:gen-class)
(:use [compojure]
[compojure.http response])
(:import [org.springframework.web.context ContextLoaderListener]
[org.springframework.web.context.support
WebApplicationContextUtils]
[com.myapp.core.model Label]))
(defn test-create-label [request]
(do
(let [spring-ctx
(WebApplicationContextUtils/getWebApplicationContext
(request :servlet-context))
label-dao (.getBean spring-ctx "labelDao")
new-label (doto (new Label)
(.setName "Label 1")
(.setDescription "testing"))]
(.create label-dao new-label)
(.read label-dao (.getId new-label)))))
(defn test-page [request]
(html [:html
[:head]
[:body
[:p "Test page."]
[:p "Label name: " (test-create-label request)]]]))
;; define routes
(defroutes webservice
(GET "/"
(test-page request))
(ANY "*"
(page-not-found)))
(defn start-server [host port]
(defserver ws-server {:host host :port port}
"/*" (servlet webservice))
(let [ctx (get-context ws-server)]
(doto ctx
(.setInitParams {"contextConfigLocation"
"classpath:/myapp-core-context.xml"})
(.addEventListener (new ContextLoaderListener)))
(start ws-server)))
(defn -main [& args]
(start-server "127.0.0.1" 8080))
The test-create-label function binds the spring framework context to
local var spring-ctx and it then binds the label-dao to a lookup of a
persistence DAO object in the spring-ctx and then the new label-new
gets bound to the new Label entity instance. Finally it creates the
new label (with relevant properties) and returns the label by reading
it from the label-dao bean.
Above works without a hitch until you do a load test with some more
concurrent calls.
Exception I get is from the hibernate framework (called in the test-
create-label during the .create process) that is configured in the
spring context:
============
org.hibernate.NonUniqueObjectException: a different object with the
same identifier value was already associated with the session:
[com.myapp.core.model.Label#13715]
============
It seems to me that multiple concurrent threads (requests) enter the
test-create-label function which results in the above exception. Could
this be the cause? If so, what is the correct way in Compojure to deal
with side-effects as in test-create-label function?
Regards,
-Alen Ribic
Firstly, like HttpServlet service() method works, multiple threads can
call the service method on single servlet instance. Thats a give.
This would mean that my test-create-label function would indeed
receive multiple simulations requests.
All good so far, until we hit the java lower-level calls that is.
So at this point I ask what concurrency solution does apply in this
scenarion. The only one I can tell is clojure "locking".
So I tried an explicit lock just around sprint-ctx calls using the
spring-ctx and locking macro as follows, and it worked:
(defn test-create-label [request]
(let [spring-ctx
(WebApplicationContextUtils/getWebApplicationContext
(request :servlet-context))
label-dao (.getBean spring-ctx "labelDao")
new-label (doto (new Label)
(.setName "Label 1")
(.setDescription "testing"))]
(locking spring-ctx
(.create label-dao new-label)
(.read label-dao (.getId new-label)))))
No to sure this is the best way to go about this. I'd appreciate any
further input.
-Alen
On Dec 17, 6:14 pm, Shantanu Kumar <kumar.shant...@gmail.com> wrote:
> URL:https://www.hibernate.org/hib_docs/v3/api/org/hibernate/NonUniqueObje...
Actually, I found a solution, hopefully optimal one.
Firstly, like HttpServlet service() method works, multiple threads can
call the service method on single servlet instance. Thats a give.
This would mean that my test-create-label function would indeed
receive multiple simulations requests.
All good so far, until we hit the java lower-level calls that is.
So at this point I ask what concurrency solution does apply in this
scenarion. The only one I can tell is clojure "locking".
So I tried an explicit lock just around sprint-ctx calls using the
spring-ctx and locking macro as follows, and it worked:
(defn test-create-label [request]
(let [spring-ctx(locking spring-ctx
(WebApplicationContextUtils/getWebApplicationContext
(request :servlet-context))
label-dao (.getBean spring-ctx "labelDao")
new-label (doto (new Label)
(.setName "Label 1")
(.setDescription "testing"))]
(.create label-dao new-label)No to sure this is the best way to go about this. I'd appreciate any
(.read label-dao (.getId new-label)))))
further input.
(defn test-create-label [request]
(let [spring-ctx
(WebApplicationContextUtils/getWebApplicationContext
(request :servlet-context))
label-dao (.getBean spring-ctx "labelDao")
new-label (Label.)]
(locking label-dao
(doto new-label
(.setName "Label 1")
(.setDescription "testing"))
(.create label-dao new-label)
(.read label-dao (.getId new-label)))))
If this is the best way, I'll probably go ahead and abstract out this
complexity into a macro.
Regards,
-Alen
On Dec 17, 6:45 pm, Shantanu Kumar <kumar.shant...@gmail.com> wrote:
Thank Shantanu. You are right. My dao instances are singles as per
spring-context default.
I also moved the setting of the setters into the locking block as it
wouldn't really work outside.
new-label (Label.)]
(defn test-create-label [request]
(let [spring-ctx
(WebApplicationContextUtils/getWebApplicationContext
(request :servlet-context))
label-dao (.getBean spring-ctx "labelDao")
(locking label-dao
(doto new-label
(.setName "Label 1")
(.setDescription "testing"))
(.create label-dao new-label)If this is the best way, I'll probably go ahead and abstract out this
(.read label-dao (.getId new-label)))))
complexity into a macro.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="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/util
http://www.springframework.org/schema/util/spring-util-2.0.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.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<!-- Data Source Setup -->
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource" destroy-
method="close">
<property name="driverClassName" value="org.hsqldb.jdbcDriver"/
>
<property name="url" value="jdbc:hsqldb:file:dev_db/myapp-
testdb"/>
<property name="username" value="sa"/>
<property name="password" value=""/>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mappingResources">
<list>
<value>mappings.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.HSQLDialect
hibernate.hbm2ddl.auto=update
</value>
</property>
</bean>
<bean id="namingStrategy"
class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
<property name="staticField">
<value>org.hibernate.cfg.ImprovedNamingStrategy.INSTANCE</
value>
</property>
</bean>
<bean id="extendedFinderNamingStrategy"
class="com.myapp.core.persist.dao.finder.impl.ExtendedFinderNamingStrategy"/
>
<!-- Dao Layer generic config-->
<bean id="finderIntroductionAdvisor"
class="com.myapp.core.persist.dao.finder.impl.FinderIntroductionAdvisor"/
>
<bean id="abstractDaoTarget"
class="com.myapp.core.persist.dao.impl.GenericDaoHibernateImpl"
abstract="true">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
<property name="namingStrategy">
<ref bean="extendedFinderNamingStrategy" />
</property>
</bean>
<bean id="abstractDao"
class="org.springframework.aop.framework.ProxyFactoryBean"
abstract="true">
<property name="interceptorNames">
<list>
<value>finderIntroductionAdvisor</value>
<value>transactionInterceptor</value>
</list>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
</bean>
<bean id="transactionInterceptor"
class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager">
<ref bean="transactionManager"/>
</property>
<property name="transactionAttributeSource">
<value>
com.myapp.core.persist.dao.impl.GenericDaoHibernateImpl.create=PROPAGATION_REQUIRED
com.myapp.core.persist.dao.impl.GenericDaoHibernateImpl.update=PROPAGATION_REQUIRED
com.myapp.core.persist.dao.impl.GenericDaoHibernateImpl.delete=PROPAGATION_REQUIRED
</value>
</property>
</bean>
<!-- Dao Layer instances -->
<bean id="labelDao" parent="abstractDao">
<property name="proxyInterfaces">
<value>com.myapp.core.persist.dao.LabelDao</value>
</property>
<property name="target">
<bean parent="abstractDaoTarget">
<constructor-arg>
<value>com.myapp.core.model.Label</value>
</constructor-arg>
</bean>
</property>
</bean>
<bean id="emailAddressDao" parent="abstractDao">
<property name="proxyInterfaces">
<value>com.myapp.core.persist.dao.EmailAddressDao</value>
</property>
<property name="target">
<bean parent="abstractDaoTarget">
<constructor-arg>
<value>com.myapp.core.model.EmailAddress</value>
</constructor-arg>
</bean>
</property>
</bean>
<bean id="senderDao" parent="abstractDao">
<property name="proxyInterfaces">
<value>com.myapp.core.persist.dao.SenderDao</value>
</property>
<property name="target">
<bean parent="abstractDaoTarget">
<constructor-arg>
<value>com.myapp.core.model.Sender</value>
</constructor-arg>
</bean>
</property>
</bean>
<bean id="billDao" parent="abstractDao">
<property name="proxyInterfaces">
<value>com.myapp.core.persist.dao.BillDao</value>
</property>
<property name="target">
<bean parent="abstractDaoTarget">
<constructor-arg>
<value>com.myapp.core.model.Bill</value>
</constructor-arg>
</bean>
</property>
</bean>
<bean id="powerBillDao" parent="abstractDao">
<property name="proxyInterfaces">
<value>com.myapp.core.persist.dao.PowerBillDao</value>
</property>
<property name="target">
<bean parent="abstractDaoTarget">
<constructor-arg>
<value>com.myapp.core.model.PowerBill</value>
</constructor-arg>
</bean>
</property>
</bean>
<!-- Business Layer instances -->
<!-- ... -->
</beans>
Regards,
-Alen
-Alen
On Dec 17, 8:22 pm, Alen Ribic <alen.ri...@gmail.com> wrote:
> That sound right. Associating the hibernate session per request would
> be the way to go then there would be no explicit locking required.
> Here is the sprint-context app xml file:
> You will see the hibernate config there too.
> Thanks Shantanu.
>
> <?xml version="1.0" encoding="UTF-8"?>
> <beans xmlns="http://www.springframework.org/schema/beans"
> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
> xmlns:context="http://www.springframework.org/schema/context"
> xmlns:jee="http://www.springframework.org/schema/jee"
> xmlns:util="http://www.springframework.org/schema/util"
> xmlns:tx="http://www.springframework.org/schema/tx"
> xmlns:aop="http://www.springframework.org/schema/aop"
> xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-2.5.xsd
> http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-2.5.xsd
> http://www.springframework.org/schema/utilhttp://www.springframework.org/schema/util/spring-util-2.0.xsd
> http://www.springframework.org/schema/jeehttp://www.springframework.org/schema/jee/spring-jee-2.5.xsd
> http://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-2.0.xsd