Sciurus
Sciurus is a collection of useful aspects to
- monitor execution runtimes of methods
- lock method executions
- cache method results
- retry method executions on exceptions
General Usage
You need to include Sciurus as dependeny and declare it as an aspect library in the aspectj-maven-plugin
. For an example, see the example project.
-
Include Sciurus as dependency
<dependency> <groupId>io.mcarle</groupId> <artifactId>sciurus</artifactId> <version>1.1.0</version> </dependency>
-
Include aspectj-maven-plugin and define Sciurus as
aspectLibrary
<plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>aspectj-maven-plugin</artifactId> <version>1.11</version> <executions> <execution> <goals> <goal>compile</goal> <goal>test-compile</goal> </goals> </execution> </executions> <configuration> <complianceLevel>${maven.compiler.source}</complianceLevel> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> <aspectLibraries> <aspectLibrary> <groupId>io.mcarle</groupId> <artifactId>sciurus</artifactId> </aspectLibrary> </aspectLibraries> </configuration> </plugin>
Annotations / Aspects
@Monitor
On all methods annotated with @Monitor
, Sciurus will measure the execution time and by default log them. Additionally, you can register custom monitors to e.g. store execution times in databases or further utilize the data.
General Usage
-
Annotate your methods, which should be monitored with Sciurus'
@Monitor
annotation.import io.mcarle.sciurus.annotation.Monitor; import io.mcarle.sciurus.annotation.LoggingMonitorParams; public class ToBeMonitored { @Monitor public int toBeMonitoredMethod(String xyz) { // do something } @Monitor @LoggingMonitorParams(warnLimit = 2000) public int shouldExecuteInUnderTwoSeconds() { // do something } }
-
As Sciurus'
@Monitor
is deactivated per default, you have to activate it yourself, e.g. at startup or with the help of a setting.import io.mcarle.sciurus.Sciurus; import org.glassfish.jersey.servlet.ServletContainer; public class ExampleServlet extends ServletContainer { @Override public void init() throws ServletException { Sciurus.startMonitor(); super.init(); } }
Logging Monitor
By default, Sciurus will log the duration together with the class and method, which will look like this
DEBUG : ( 150 ms) - io.mcarle.example.sciurus.resources.ExampleResource#longRunningProcess
@LoggingMonitorParams
With the help of @LoggingMonitorParams
, you can add some additional information to Sciurus' logging monitor:
warnLimit
andwarnLimitUnit
can be used to define an amount of time. If the method execution takes longer, the log level will be warning, otherwise debug.logParameter
: When true, the log message also contains the actual parameters of the methodlogResult
: When true, the log message also contains the result of the method
When all parameters are set, the result may look like this
WARN : ( 200 ms) - io.mcarle.example.sciurus.resources.ExampleResource#longRunningProcess([150]) : 42
Custom Monitor
You can register custom monitor which will get notified after each @Monitor
annotated method finished its execution.
Sciurus.registerMonitor(new CustomMonitor() {
@Override
public void monitored(Duration duration, String declaringTypeName, Method method, Object[] methodArgs, Throwable throwable, Class returnType, Object returnValue) {
// do something
}
});
Note: You can also deregister the logging monitor, if you do not need it: Sciurus.deregisterMonitor(LoggingMonitor.INSTANCE)
@Cache
On all methods annotated with @Cache
, before execution, Sciurus will check in the defined cache (global, map, or some custom cache) if there is a result stored for this method. If that is the case, that value is returned and the actual method execution will be skipped. When there is no such value, Sciurus will store it in the defined cache after executing the method.
Note: Sciurus does not distinguish the method call from various instances. Therefore, using it on POJOs is not recommended. A good example would be a proxy or a database repository.
General Usage
import io.mcarle.sciurus.annotation.Cache;
import java.time.temporal.ChronoUnit;
public class ToBeCached {
@Cache(
time = 2,
unit = ChronoUnit.SECONDS,
cacheName = <Sciurus.CACHE_GLOBAL or Sciurus.CACHE_MAP or some custom cache name>
)
public String shouldBeCached(int param) {
// do domething
}
}
Custom Caches
Sciurus comes with an in memory expiring map cache as the default one. You may register custom caches and define them in the @Cache
annotation as the target cache.
Note: Every custom cache must return CustomCache.EMPTY
on get, if no cache entry is present!
Sciurus.registerCache("<name of your cache>", new CustomCache() {
@Override
public Object get(ExecutionIdentifier executionIdentifier) {
// Loading from cache or return CustomCache.EMPTY
}
@Override
public void put(ExecutionIdentifier executionIdentifier, Object result, Duration duration) {
// Saving to cache
}
});
@Lock
Before executing a method annotated with @Lock
, Sciurus will check if there is any running instance of that method and wait till that execution is finished before starting. You may ask, how this is different from javas synchronize?
-
It works on any instance of the class If the same method of a class is being called in parallel on two different instances, then the methods are executed successively.
-
It can include method parameters as condition If the same method of a class is being called in parallel three times, all with different parameters, then the methods are all executed in parallel. If one call has the same parameters as another, the later one waits until the first execution is finished.
exampleMethod("A") exampleMethod("B") exampleMethod("A") check -> ok store exec info execute check -> ok check -> not ok ... store exec info wait ... execute ... finished ... ... remove exec info ... ... notify finished ... remove exec info check -> ok notify store exec info execute Note: This table is a simplified scenario. Sciurus will also work on more complex scenarios!
@Retry
When a method annotated with @Retry
throws an exception, then Sciurus will execute the method again for the amount of specified retries.
Use Case Examples
There are a number of possible use cases for each aspect.
@Monitor
- Notify a monitoring application
- Find bottlenecks
- Test the speed of a new software version before deploying it in production
- Store execution times (e.g. in a database) and
- see how they evolve over time
- see runaway values
- monitor them through a MBean
@Cache
- Caching (surprise!)
- Facade for various caches which can be exchanged with merely little code changes
@Lock
- Prevent multiple storing (e.g. in databases)
- Prevent multiple execution of long running or high cpu consuming methods (e.g. in combination with
@Cache
)
@Retry
- Retry storing entities into the database due to locking exceptions
- Retry a failed connection to a web service
- Retry a method which may lead to some random or temporary incidents
License
Unless explicitly stated otherwise all files in this repository are licensed under the Apache Software License 2.0
Copyright 2017 Marcel Carlé
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.