You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2012/01/26 18:33:15 UTC

svn commit: r1236308 - in /tapestry/tapestry5/trunk/tapestry-ioc/src: main/java/org/apache/tapestry5/ioc/annotations/ main/java/org/apache/tapestry5/ioc/internal/services/ main/java/org/apache/tapestry5/ioc/services/ test/java/org/apache/tapestry5/ioc/...

Author: hlship
Date: Thu Jan 26 17:33:14 2012
New Revision: 1236308

URL: http://svn.apache.org/viewvc?rev=1236308&view=rev
Log:
TAP5-1168: Add @Operation annotation that can be used to automatically track a component or service method invocation using the OperationTracker

Added:
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/annotations/Operation.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/OperationAdvisorImpl.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/OperationAdvisor.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/services/OperationAdvisorTest.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/services/OperationTrackedModule.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/services/OperationTrackedService.java
Modified:
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/TapestryIOCModule.java

Added: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/annotations/Operation.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/annotations/Operation.java?rev=1236308&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/annotations/Operation.java (added)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/annotations/Operation.java Thu Jan 26 17:33:14 2012
@@ -0,0 +1,39 @@
+// Copyright 2012 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.ioc.annotations;
+
+import java.lang.annotation.*;
+
+/**
+ * Describes a method as one that should be operation tracked. Operation tracking is useful when an exception in deeply nested code occurs,
+ * as it is possible to identify (using human readable descriptions) the path to the code that failed.
+ *
+ * @see org.apache.tapestry5.ioc.OperationTracker
+ * @since 5.4
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@UseWith({AnnotationUseContext.SERVICE, AnnotationUseContext.COMPONENT, AnnotationUseContext.PAGE})
+public @interface Operation
+{
+    /**
+     * The message to pass to {@link org.apache.tapestry5.ioc.OperationTracker#invoke(String, org.apache.tapestry5.ioc.Invokable)}. If the message contains
+     * the '%' character, it is interpreted to be a {@linkplain java.util.Formatter format string}, passed the method's parameters.
+     *
+     * @see org.apache.tapestry5.ioc.services.OperationAdvisor#createAdvice(String)
+     */
+    String value();
+}

Added: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/OperationAdvisorImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/OperationAdvisorImpl.java?rev=1236308&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/OperationAdvisorImpl.java (added)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/OperationAdvisorImpl.java Thu Jan 26 17:33:14 2012
@@ -0,0 +1,129 @@
+// Copyright 2012 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.ioc.internal.services;
+
+import org.apache.tapestry5.ioc.MethodAdviceReceiver;
+import org.apache.tapestry5.ioc.OperationTracker;
+import org.apache.tapestry5.ioc.annotations.Operation;
+import org.apache.tapestry5.ioc.annotations.PreventServiceDecoration;
+import org.apache.tapestry5.ioc.internal.util.InternalUtils;
+import org.apache.tapestry5.ioc.services.OperationAdvisor;
+import org.apache.tapestry5.plastic.MethodAdvice;
+import org.apache.tapestry5.plastic.MethodInvocation;
+
+import java.lang.reflect.Method;
+
+@PreventServiceDecoration
+public class OperationAdvisorImpl implements OperationAdvisor
+{
+    private final OperationTracker tracker;
+
+    public OperationAdvisorImpl(OperationTracker tracker)
+    {
+        this.tracker = tracker;
+    }
+
+    private Runnable toRunnable(final MethodInvocation invocation)
+    {
+        return new Runnable()
+        {
+            @Override
+            public void run()
+            {
+                invocation.proceed();
+            }
+        };
+    }
+
+    private class SimpleAdvice implements MethodAdvice
+    {
+        private final String description;
+
+        SimpleAdvice(String description)
+        {
+            this.description = description;
+        }
+
+        @Override
+        public void advise(MethodInvocation invocation)
+        {
+            tracker.run(description, toRunnable(invocation));
+        }
+    }
+
+    private class FormattedAdvice implements MethodAdvice
+    {
+        private final String format;
+
+        FormattedAdvice(String format)
+        {
+            this.format = format;
+        }
+
+        @Override
+        public void advise(MethodInvocation invocation)
+        {
+            Object[] parameters = extractParameters(invocation);
+
+            String description = String.format(format, parameters);
+
+            tracker.run(description, toRunnable(invocation));
+        }
+
+        private Object[] extractParameters(MethodInvocation invocation)
+        {
+            int count = invocation.getMethod().getParameterTypes().length;
+
+            Object[] result = new Object[count];
+
+            for (int i = 0; i < count; i++)
+            {
+                result[i] = invocation.getParameter(i);
+            }
+
+            return result;
+        }
+    }
+
+    @Override
+    public void addOperationAdvice(MethodAdviceReceiver receiver)
+    {
+        for (Method m : receiver.getInterface().getMethods())
+        {
+
+            Operation annotation = receiver.getMethodAnnotation(m, Operation.class);
+
+            if (annotation != null)
+            {
+                String value = annotation.value();
+
+                receiver.adviseMethod(m, createAdvice(value));
+            }
+        }
+    }
+
+    @Override
+    public MethodAdvice createAdvice(String description)
+    {
+        assert InternalUtils.isNonBlank(description);
+
+        if (description.contains("%"))
+        {
+            return new FormattedAdvice(description);
+        }
+
+        return new SimpleAdvice(description);
+    }
+}

Added: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/OperationAdvisor.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/OperationAdvisor.java?rev=1236308&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/OperationAdvisor.java (added)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/OperationAdvisor.java Thu Jan 26 17:33:14 2012
@@ -0,0 +1,42 @@
+// Copyright 2012 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.ioc.services;
+
+import org.apache.tapestry5.ioc.MethodAdviceReceiver;
+import org.apache.tapestry5.plastic.MethodAdvice;
+
+/**
+ * Used from a {@linkplain org.apache.tapestry5.ioc.annotations.Advise service advice method} to identify methods with the
+ * {@link org.apache.tapestry5.ioc.annotations.Operation} annotation, and add advice for those methods. This advice should typically
+ * be provided first, or nearly first, among all advice, to maximize the benefit of tracking operations.
+ *
+ * @since 5.4
+ */
+public interface OperationAdvisor
+{
+    /**
+     * Adds {@linkplain #createAdvice advice} to methods with the {@link org.apache.tapestry5.ioc.annotations.Operation} annotation.
+     */
+    void addOperationAdvice(MethodAdviceReceiver receiver);
+
+    /**
+     * Creates advice for a method.
+     *
+     * @param description the text (or format) used to display describe the operation for the method
+     * @return method advice
+     * @see org.apache.tapestry5.ioc.annotations.Operation#value()
+     */
+    MethodAdvice createAdvice(String description);
+}

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/TapestryIOCModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/TapestryIOCModule.java?rev=1236308&r1=1236307&r2=1236308&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/TapestryIOCModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/TapestryIOCModule.java Thu Jan 26 17:33:14 2012
@@ -73,6 +73,7 @@ public final class TapestryIOCModule
         binder.bind(ThunkCreator.class, ThunkCreatorImpl.class);
         binder.bind(UpdateListenerHub.class, UpdateListenerHubImpl.class).preventReloading();
         binder.bind(PeriodicExecutor.class, PeriodicExecutorImpl.class);
+        binder.bind(OperationAdvisor.class, OperationAdvisorImpl.class);
     }
 
     /**

Added: tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/services/OperationAdvisorTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/services/OperationAdvisorTest.java?rev=1236308&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/services/OperationAdvisorTest.java (added)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/services/OperationAdvisorTest.java Thu Jan 26 17:33:14 2012
@@ -0,0 +1,111 @@
+package org.apache.tapestry5.ioc.services;
+
+import org.apache.tapestry5.ioc.Invokable;
+import org.apache.tapestry5.ioc.LoggerSource;
+import org.apache.tapestry5.ioc.OperationTracker;
+import org.apache.tapestry5.ioc.Registry;
+import org.apache.tapestry5.ioc.def.ModuleDef;
+import org.apache.tapestry5.ioc.internal.DefaultModuleDefImpl;
+import org.apache.tapestry5.ioc.internal.LoggerSourceImpl;
+import org.apache.tapestry5.ioc.internal.RegistryImpl;
+import org.apache.tapestry5.ioc.internal.services.PlasticProxyFactoryImpl;
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
+import org.apache.tapestry5.ioc.test.IOCTestCase;
+import org.slf4j.Logger;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import java.util.List;
+
+/**
+ * Tests for the {@link OperationAdvisor} service.
+ *
+ * @since 5.4
+ */
+public class OperationAdvisorTest extends IOCTestCase
+{
+    private List<String> operations = CollectionFactory.newList();
+
+    private Registry registry;
+
+    @BeforeClass
+    public void setup()
+    {
+        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+        LoggerSource loggerSource = new LoggerSourceImpl();
+
+        Logger logger = loggerSource.getLogger(OperationAdvisorTest.class);
+        Logger proxyFactoryLogger = loggerSource.getLogger(TapestryIOCModule.class.getName() + ".PlasticProxyFactory");
+
+        PlasticProxyFactory plasticProxyFactory = new PlasticProxyFactoryImpl(classLoader, proxyFactoryLogger);
+
+        List<ModuleDef> modules = CollectionFactory.newList();
+
+        modules.add(new DefaultModuleDefImpl(TapestryIOCModule.class, logger, plasticProxyFactory));
+        modules.add(new DefaultModuleDefImpl(OperationTrackedModule.class, logger, plasticProxyFactory));
+
+        OperationTracker simpleOperationTracker = new OperationTracker()
+        {
+            @Override
+            public void run(String description, Runnable operation)
+            {
+                operations.add(description);
+
+                operation.run();
+            }
+
+            @Override
+            public <T> T invoke(String description, Invokable<T> operation)
+            {
+                operations.add(description);
+
+                return operation.invoke();
+            }
+        };
+
+        registry = new RegistryImpl(modules, plasticProxyFactory, loggerSource, simpleOperationTracker);
+    }
+
+    @AfterClass
+    public void cleanup()
+    {
+        registry.shutdown();
+
+        registry = null;
+
+        operations = null;
+    }
+
+    @Test
+    public void simple_operation_tracking()
+    {
+        OperationTrackedService service = registry.getService(OperationTrackedService.class);
+
+        service.nonOperation();
+
+        operations.clear();
+
+        service.first();
+
+        assertListsEquals(operations, "First operation");
+    }
+
+    @Test
+    public void complex_operation_tracking()
+    {
+        OperationTrackedService service = registry.getService(OperationTrackedService.class);
+
+        service.nonOperation();
+
+        operations.clear();
+
+        service.second("foo");
+
+        service.second("bar");
+
+        assertListsEquals(operations, "Second operation: foo", "Second operation: bar");
+    }
+
+
+}

Added: tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/services/OperationTrackedModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/services/OperationTrackedModule.java?rev=1236308&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/services/OperationTrackedModule.java (added)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/services/OperationTrackedModule.java Thu Jan 26 17:33:14 2012
@@ -0,0 +1,23 @@
+package org.apache.tapestry5.ioc.services;
+
+import org.apache.tapestry5.ioc.MethodAdviceReceiver;
+import org.apache.tapestry5.ioc.annotations.Advise;
+import org.apache.tapestry5.ioc.annotations.Match;
+import org.apache.tapestry5.ioc.annotations.Order;
+
+public class OperationTrackedModule
+{
+    public OperationTrackedService buildTestSubject(DefaultImplementationBuilder builder)
+    {
+        return builder.createDefaultImplementation(OperationTrackedService.class);
+    }
+
+    @Advise @Match("*")
+    @Order("before:*")
+    public void addOperationTracking(MethodAdviceReceiver receiver, OperationAdvisor advisor)
+    {
+        advisor.addOperationAdvice(receiver);
+    }
+
+
+}

Added: tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/services/OperationTrackedService.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/services/OperationTrackedService.java?rev=1236308&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/services/OperationTrackedService.java (added)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/services/OperationTrackedService.java Thu Jan 26 17:33:14 2012
@@ -0,0 +1,14 @@
+package org.apache.tapestry5.ioc.services;
+
+import org.apache.tapestry5.ioc.annotations.Operation;
+
+public interface OperationTrackedService
+{
+    @Operation("First operation")
+    void first();
+
+    @Operation("Second operation: %s")
+    void second(String name);
+
+    void nonOperation();
+}