{"id":10,"date":"2009-07-07T20:44:40","date_gmt":"2009-07-07T19:44:40","guid":{"rendered":"http:\/\/katastrophos.net\/magnus\/blog\/?p=10"},"modified":"2009-07-12T22:03:04","modified_gmt":"2009-07-12T21:03:04","slug":"detached-service-methods-with-spring-aop","status":"publish","type":"post","link":"https:\/\/jungsbluth.de\/magnus\/blog\/2009\/07\/07\/detached-service-methods-with-spring-aop\/","title":{"rendered":"Detached Service Methods with Spring AOP"},"content":{"rendered":"<p>In a typical spring based server application there is a service layer responsible for infrastructure tasks and communication with the clients.  Service methods can be long running and execution would normally block the user-interface. Putting the client side end of the call in a thread solves the blocking but leaves the network connection open while the server side of the call is active. This can lead to all sorts of time-out issues and seems to be the wrong end to fix the problem .<\/p>\n<p>Using spring AOP it is remarkably easy to declaratively enable service methods to execute detached from the regular call context.<\/p>\n<h4>The Goal<\/h4>\n<pre  name=\"code\" class=\"java\">@DeferredExecution\r\npublic myServiceMethod() throws DeferredExecutionException {\r\n}<\/pre>\n<p>This method would execute with the following sematics:<\/p>\n<ol>\n<li>The regular method call is wrapped in a thread<\/li>\n<li>The thread is executed<\/li>\n<li>If the thread does not finish within x seconds, raise a checked exception<\/li>\n<li>If it does finish in time, return the result of the method<\/li>\n<\/ol>\n<p>This way clients of this method are forced to handle detached execution (i.e. show a progress dialog querying the server) and the client side of the call not langer lasts longer than a set time.<br \/>\n<!--more--><\/p>\n<h4>Implementation<\/h4>\n<p>Define a new Annotation<\/p>\n<pre  name=\"code\" class=\"java\">\r\n@Target(ElementType.METHOD)\r\n@Retention(RetentionPolicy.RUNTIME)\r\npublic @interface DeferredExecution {\r\n}<\/pre>\n<p>Define a spring aspect (note that altough it uses AspectJ annotations, AspectJ is not required)<\/p>\n<pre  name=\"code\" class=\"java\">\r\n@Aspect\r\npublic class DeferredExecutionInterceptor implements Ordered {\r\n\r\n\tprivate int order;\r\n\r\n\t@Pointcut(\"execution(* *.*(..)) &amp;&amp; @annotation(DeferredExecution)\")\r\n\tpublic void inServiceMethod() {}\r\n\r\n\t@Around(\"inServiceMethod()\")\r\n\tpublic Object doDeferredExecution(ProceedingJoinPoint joinPoint) \r\n\t\t\tthrows Throwable {\r\n\t\tJoinPointExecutionThread thread = new JoinPointExecutionThread(joinPoint);\r\n\t\tthread.start();\r\n\r\n\t\t\/\/wait for the thread to finish and\r\n\t\t\/\/- either throw a DeferredExecutionException\r\n\t\t\/\/- or return the result\r\n\t}\r\n\r\n\t@Override\r\n\tpublic int getOrder() {\r\n\t\treturn order;\r\n\t}\r\n\r\n\tpublic void setOrder(int order) {\r\n\t\tthis.order = order;\r\n\t}\r\n}<\/pre>\n<p>It is important to implement the Ordered interface since you most likely want to influence the order of the created AOP-Proxy. It should run after a security advisor and before any transaction interceptor.<\/p>\n<p>The following spring config brings this aspect to life. The aspect is applied to all spring managed instances.<\/p>\n<pre name=\"code\" class=\"xml\">\r\n&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\r\n&lt;beans \r\n  xmlns=\"http:\/\/www.springframework.org\/schema\/beans\"\r\n  xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\"\r\n  xmlns:aop=\"http:\/\/www.springframework.org\/schema\/aop\"\r\n  xsi:schemaLocation=\"\r\n    http:\/\/www.springframework.org\/schema\/beans \r\n    http:\/\/www.springframework.org\/schema\/beans\/spring-beans-2.5.xsd \r\n    http:\/\/www.springframework.org\/schema\/aop \r\n    http:\/\/www.springframework.org\/schema\/aop\/spring-aop-2.5.xsd\"&gt;\r\n\r\n    &lt;aop:aspectj-autoproxy \/&gt;\r\n\r\n    &lt;bean id=\"deferredExecutionInterceptor\" class=\"DeferredExecutionInterceptor\"&gt;\r\n        &lt;property name=\"order\" value=\"100\" \/&gt;\r\n    &lt;\/bean&gt;\r\n\r\n    &lt;bean id=\"testService\" class=\"TestServiceImpl\"&gt;\r\n    &lt;\/bean&gt;\r\n&lt;\/beans&gt;\r\n<\/pre>\n<p>There is one tiny bit that is optional: Nothing prevents a developer not to declare a throws clause on an annotated service method. Since client code <strong>must<\/strong> handle the case when execution is detached, this is crucial. <\/p>\n<p>To enforce this at <strong>compile time<\/strong> we can use AspectJ&#8217;s declare error feature to generate a compile error when such an inconsistency is found.<\/p>\n<p>Create a <strong>separate<\/strong> Aspect with<\/p>\n<pre  name=\"code\" class=\"java\">\r\npublic aspect DeferredExecutionMethodCheckAspect {\r\n\t\t\r\n\tpublic pointcut deferredExecutionMethodWithoutThrows(): \r\n\t\texecution(@DeferredExecution public * *.* (..)) \r\n\t\t&& !execution(@DeferredExecution public * *.* (..) \r\n\t\t\t\tthrows DeferredExecutionException);\r\n\t\r\n\tdeclare error: deferredExecutionMethodWithoutThrows(): \r\n\t\t\"Method must declare throws DeferredExecutionException for annotation to work\";\r\n}\r\n<\/pre>\n<p>When configuring compile time weaving in maven or AJDT it is crucial <strong>not to weave<\/strong> DeferredExecutionInterceptor, since this would make it impossible to set the order in which spring executes the AOP Proxies. <\/p>\n<h4>Remarks<\/h4>\n<p>This is of course only a rough sketch but it worked almost instantly. Note that there is no documentation concerning the thread safety of the joinPoint passed into the around advice. Since the joinPoint is only used within one thread it seems safe to to so. <\/p>\n<p>In a real world scenario you would most likely not use threads directly but your choice for handling background jobs (for example backed by Quartz).<\/p>\n<p>Note that there is no mechanism to handle cascaded service calls. You would most likely want only one background job created along the way. This could easily be solved using ThreadLocals&#8230;<\/p>\n<p><script src=\"js\/shCore.js\"><\/script> <script src=\"js\/shBrushCSharp.js\"><\/script><br \/>\n<script src=\"js\/shBrushXml.js\"><\/script> <script type=\"text\/javascript\"><!--\ndp.SyntaxHighlighter.ClipboardSwf = '\/flash\/clipboard.swf';\ndp.SyntaxHighlighter.HighlightAll('code');\n\/\/ --><\/script><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In a typical spring based server application there is a service layer responsible for infrastructure tasks and communication with the clients. Service methods can be long running and execution would normally block the user-interface. Putting the client side end of the call in a thread solves the blocking but leaves the network connection open while [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[8,9,23,7],"tags":[21,22,10,11,20],"_links":{"self":[{"href":"https:\/\/jungsbluth.de\/magnus\/blog\/wp-json\/wp\/v2\/posts\/10"}],"collection":[{"href":"https:\/\/jungsbluth.de\/magnus\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/jungsbluth.de\/magnus\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/jungsbluth.de\/magnus\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/jungsbluth.de\/magnus\/blog\/wp-json\/wp\/v2\/comments?post=10"}],"version-history":[{"count":35,"href":"https:\/\/jungsbluth.de\/magnus\/blog\/wp-json\/wp\/v2\/posts\/10\/revisions"}],"predecessor-version":[{"id":46,"href":"https:\/\/jungsbluth.de\/magnus\/blog\/wp-json\/wp\/v2\/posts\/10\/revisions\/46"}],"wp:attachment":[{"href":"https:\/\/jungsbluth.de\/magnus\/blog\/wp-json\/wp\/v2\/media?parent=10"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/jungsbluth.de\/magnus\/blog\/wp-json\/wp\/v2\/categories?post=10"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/jungsbluth.de\/magnus\/blog\/wp-json\/wp\/v2\/tags?post=10"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}