{"id":16456091,"url":"https://github.com/amitjoy/aspecio","last_synced_at":"2025-03-23T10:32:42.811Z","repository":{"id":103152426,"uuid":"220509881","full_name":"amitjoy/aspecio","owner":"amitjoy","description":"AOP Proxies for OSGi Services","archived":false,"fork":false,"pushed_at":"2024-11-05T09:44:22.000Z","size":662,"stargazers_count":7,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-18T19:51:58.602Z","etag":null,"topics":["aop","aop-aspects","aspect-oriented-programming","cross-cutting-concerns","interception","interceptor","java","osgi"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/amitjoy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-11-08T16:50:03.000Z","updated_at":"2024-11-05T09:44:27.000Z","dependencies_parsed_at":null,"dependency_job_id":"8da0778d-6071-4099-8644-06d95a45a3a3","html_url":"https://github.com/amitjoy/aspecio","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amitjoy%2Faspecio","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amitjoy%2Faspecio/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amitjoy%2Faspecio/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amitjoy%2Faspecio/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/amitjoy","download_url":"https://codeload.github.com/amitjoy/aspecio/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245090847,"owners_count":20559296,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aop","aop-aspects","aspect-oriented-programming","cross-cutting-concerns","interception","interceptor","java","osgi"],"created_at":"2024-10-11T10:24:44.466Z","updated_at":"2025-03-23T10:32:42.488Z","avatar_url":"https://github.com/amitjoy.png","language":"Java","readme":"\u003cp align='center'\u003e\n    \u003cimg width=\"350\" alt=\"logo\" src=\"https://user-images.githubusercontent.com/13380182/69403424-d8fc6b80-0cfa-11ea-888d-cbb9b64acc68.png\"\u003e\n\u003c/p\u003e\n\n# Aspect Oriented Programming (AOP) Proxies for OSGi Services ![Build Status](https://travis-ci.org/amitjoy/aspecio.svg?branch=master)\n\nAspecio is a `micro-framework` providing `AOP Proxies` in OSGi environment. It brings a mix of component-oriented and aspect-oriented programming to your OSGi application. Aspecio lets you define `Aspects` that you can later pick to add behavior to your service components and avoid duplicating boilerplate dealing with cross-cutting concerns.\n\n## Aspecio 1.0.0\n\n`Aspecio 1.0.0` is the complete overhaul with a completely new proxy model that is simpler, completely unrestricted and faster. Refer to [Primeval Reflex](http://github.com/primeval-io/primeval-reflex) for the proxy capabilities.\n\n## Documentation\n\nIn addition to this README, the API javadoc provides an extensive overview.\n\n## Requirements\n\n1. Java 8+\n2. OSGi R7\n\n## Overview\n\n### Why Aspects?\n\nIn general, aspects enable you to intercept code and alter its execution behavior. However, there are several downsides to using aspects:\n\n* Scattering behavior across the codebase\n* Making the execution model opaque by having aspects intercept any random piece of code, including internal code that might have implicit invariants that aspects break\n* Not knowing **which** aspects are being _woven_ on a piece of code at a given time\n* Having some aspect framework implementations _weave_ aspects into one big bytecode muddy-ball, making debugging difficult when line numbers are desynchronized, adding synthetic methods in the bytecode\n* Sometimes aspects are implemented using Java proxies which can break consuming code, for example, code relying on reflection such as annotation-driven frameworks\n\nHowever, there are _cross-cutting concerns_ for which aspects can be beneficial, for example:\n\n* **Security**: ensuring some conditions are met before being allowed into a function ; \n* **Metrics**: having live metrics on critical components (e.g. using Coda Hale's Metrics library) ;\n* Ensuring a piece of code takes place in a transaction;\n* And many more :-)\n\nAspecio aims to make aspects predictable and bridges them with the OSGi service registry model.\n\n### Aspecio and OSGi\n\nWhile Aspecio's internal weaving code can be interesting to plug into other Dependency Injection frameworks, it currently does support OSGi R7 exclusively out of the box.\n\nAspecio works with OSGi services and can weave any _willing_ OSGi service. \n\nAspecio works with any service scope, `singleton`, `bundle` and `prototype` and will only create as many instances as expected. In case of service frameworks using the `bundle` scope to make service creation lazy (such as Declarative Services), but still having effectively `singleton` services, Aspecio will make sure each service instance has precisely one proxy instance.\n\nThanks to relying on OSGi's low-level service primitives, Aspecio can work with any OSGi service component framework, including any compliant implementation of Declarative Services, Blueprint, Guice + Peaberry, Apache Felix iPojo or Apache Felix Dependency Manager.\n\nAspecio has been tested on Felix 6.0.3 but should work with any framework compliant with OSGi R7.\n\nIn the following examples, Declarative Services (DS) is used.\n\n\n### Aspecio's Weaving\n\nAspecio does select service objects that ask for certain aspects in their service properties, hiding (by default) the original service from all bundles except the system bundle and Aspecio itself.\n\nAspecio uses [Primeval Reflex](http://github.com/primeval-io/primeval-reflex) to proxy services requesting weaving. All interfaces and public methods are proxied.\n\nSee [Primeval Reflex](http://github.com/primeval-io/primeval-reflex) for documentation on the proxies and writing interceptors.\n\n### Drawback\n\nOSGi services registered as simple classes cannot be woven using Aspecio. The services need to implement well-defined exported service APIs or interfaces.\n\n### Installing Aspecio in an OSGi Framework\n\nInstall `com.amitinside.aspecio.provider` to your OSGi framework, and it will work right away.\n\nIf there are already registered services with the weaving property, Aspecio will restart their bundles to make sure it has the opportunity to apply its service hook.\n\nAspecio first collects the set of bundles providing services to weave, sorts them using the natural `Bundle` comparator (based on bundle IDs). It stops them all in that order, then starts them all. The ordering should allow to keep the original installation order, and stopping and starting them by batch is aiming to minimize service level interactions between these bundles (such as re-creating components with static references too many times).\n\n\n## Defining an aspect with Aspecio\n\nIn Aspecio, we use Java to declare an Aspect.\n\nHere is a simple Aspect counting how many times a method has been invoked. Depending on its configuration, it may count only successful calls (e.g., methods that did not throw an exception) or all methods indiscriminately. \n\n```java\n@Component\n@Aspect(name = CountingAspect.class)\npublic final class CountingAspectImpl implements Interceptor {\n\n    private final Map\u003cMethod, Integer\u003e methodCallCount = new ConcurrentHashMap();\n    \n    // This could be dynamically configured using Declarative Service + ConfigAdmin\n    private volatile boolean countOnlySuccessful = false;\n\n    @Override\n    public \u003cT, E extends Throwable\u003e T onCall(CallContext context, InterceptionHandler\u003cT\u003e handler) throws E {\n        if (countOnlySuccessful) {\n            T res = handler.invoke();\n            methodCallCount.compute(context.method, (k, v) -\u003e v == null ? 1 : (v += 1));\n            return res;\n        } else {\n            methodCallCount.compute(context.method, (k, v) -\u003e v == null ? 1 : (v += 1));\n            return handler.invoke();\n        }\n    }    \n}\n```\n\nAspecio finds aspects by:\n\n* Looking for OSGi services; in the example above, provided using the `@Component` Declarative Service annotation)\n* That provide the OSGi service string property `AspecioConstants.SERVICE_ASPECT` (`\"service.aspect.name\"`) declared using the `@Aspect` annotation.\n* That implements the interface `io.primeval.reflect.proxy.Interceptor` (it need not be provided as the service's `\"objectClass\"`).\n* If several services provide the same aspect, Aspecio will pick the one with the highest-service ranking; in case of equal service rankings, Aspecio will pick the one with the lowest service ID. Aspecio supports OSGi's service dynamics and will gladly replace or update Aspects' lifecycles. Aspecio is always 'greedy': if a \"better\" interceptor is registered for a given aspect, all the services using it will have it updated immediately. \n\nIn the example above, the component `CountingAspectImpl` provides the aspect named `\"CountingAspect\"` (a Java String). You can call your aspects with any string, but it is practical to use Java classes to piggyback on the namespaces. \n\nFor documentation on Interceptors, see [Primeval Reflect](http://github.com/primeval-io/primeval-reflect).\n\n\n## Aspect Weaving with Aspecio\n\nIn Aspecio, we can only weave OSGi services that opt-in to one or several aspects. This is because services have a well-defined contract and make it the perfect entry point for aspects.\n\nServices must declare the OSGi service `String` or `String[]` property `service.aspect.weave.required` (for required aspects) or `\"service.aspect.weave.optional\"` (for optional aspects), with the aspect names as value, to be candidate for weaving.\n\n### Example\n\nHere is a Declarative Services component that will be woven by Aspecio using our `CountingAspect` declared earlier.\n\n```java\n@Component\n@Weave(required = CountingAspect.class, optional = AnotherOptionalAspect.class)\npublic final class HelloGoodbyeImpl implements Hello, Goodbye {\n\n    @Override\n    public String hello() {\n        return \"hello\";\n    }\n\n    @Override\n    public String goodbye() {\n        return \"goodbye\";\n    }\n\n}\n```\n\nThat's all! Now any aspect woven will be notified with the calls of methods `hello()` or `goodbye()` and may interact by returning other values, throwing exceptions, catching exceptions, accessing the arguments of each call (or just some) or even update the arguments before the call takes place.\n\nAlso, because `\"CountingAspect.class\"` is `required` by `HelloGoodbyeImpl`, the service will **not** be visible until a service providing Aspect `\"CountingAspect.class\"` is available. All the kinds of OSGi dynamics can happen here: the aspect can be registered after a service requiring it or later.\n\nHaving `\"AnotherOptionalAspect.class\"` as an optional aspect will not prevent Aspecio's proxy of `HelloGoodbyeImpl` of being registered even in case `\"AnotherOptionalAspect.class\"` is missing; however, if it becomes available during `HelloGoodbyImpl`'s lifetime, it will start intercepting its methods as well.\n\n\n## Aspect Patterns\n\n### Annotation-based Interception\n\n* When you want to intercept only specific annotated methods, and you can use the annotation to pass configuration to the interceptor\n* When you annotate specific method parameters to guide your aspect\n\n```java\n@Component\n@Aspect(provides = MyAnnotationDrivenAspect.class)\npublic final class MyAnnotationDrivenAspectImpl implements AnnotationInterceptor\u003cMyAnnotation\u003e {\n\n    @Override\n    public \u003cT, E extends Throwable\u003e T onCall(MyAnnotationDrivenAspect annotation, CallContext context,\n                                                              InterceptionHandler\u003cT\u003e handler) throws E {\n        // may contain previous info on how to use the aspect.\n        ...\n    }\n    \n    @Override\n    public Class\u003cMyAnnotation\u003e intercept() {\n        return MyAnnotation.class;\n    }\n}\n```\n\n\nSee `AnnotationInterceptor` in [Primeval Reflex](http://github.com/primeval-io/primeval-reflex).\n\n\n### Aspects bridging services\n\nBecause we rarely want the actual cross-cutting behaviour to reside in our interceptor, it is a better approach to use your favourite component framework to make your aspects merely bring a functionality provided elsewhere:\n\n\n```java\n@Component\n@Aspect(provides = MyFeatureAspect.class)\npublic final class MyFeatureAspectImpl implements Interceptor {\n\n    @Reference\n    private MyFeature myFeatureService; // logic is in another service\n\n    @Override\n    public \u003cT, E extends Throwable\u003e T onCall(MyAnnotationDrivenAspect annotation, CallContext context,\n                                                              InterceptionHandler\u003cT\u003e handler) throws E {\n       // use MyFeature service\n       ....\n    }                                                              \n}\n```\n\n### Interceptors registering extra service properties\n\n\n```java\n@Component\n@Aspect(provides = MySecurityAspect.class, extraProperties = \"secured\")\npublic final class MySecurityAspectImpl implements Interceptor {\n\n    @Reference private Auth auth;\n    @Override\n    public \u003cT, E extends Throwable\u003e T onCall(MyAnnotationDrivenAspect annotation, CallContext context,\n                                                              InterceptionHandler\u003cT\u003e handler) throws E {\n         auth.checkPermissions(...);\n         ...\n    }   \n}\n```\n\nThe proxy service object registered by Aspecio will have the OSGi service boolean property `\"secured\"` set to `Boolean.TRUE`. Now consuming code can check for that property using a target filter to know if a service is secure. The consuming code doesn't need to know whether a service was obtained manually or using an aspect.\n\n\n## Debugging Aspecio\n\nAspecio provides a service aptly named `Aspecio` that can show you what Aspecio sees in runtime:\n\n* which aspects are available\n* which services are woven\n\nAspecio provides two Gogo commands to get the same information in the Gogo shell, `aspecio:aspects` and `aspecio:woven`.\n\nHere's a sample output of these two commands:\n\n```\ng! aspects\n* com.amitinside.aspecio.examples.aspect.metric.MetricAspect$All\n  [ --- ACTIVE --- ] Service ID 29, class com.amitinside.aspecio.examples.aspect.metric.internal.AllMetricInterceptorImpl, extra properties: [measured]\n                     Provided by: com.amitinside.aspecio.examples 1.0.0.201911130609 [10]\n\n* com.amitinside.aspecio.examples.aspect.counting.CountingAspect\n  [ --- ACTIVE --- ] Service ID 28, class com.amitinside.aspecio.examples.aspect.counting.internal.CountingAspectImpl, extra properties: []\n                     Provided by: com.amitinside.aspecio.examples 1.0.0.201911130609 [10]\n\n* com.amitinside.aspecio.examples.aspect.metric.MetricAspect$AnnotatedOnly\n  [ --- ACTIVE --- ] Service ID 30, class com.amitinside.aspecio.examples.aspect.metric.internal.AnnotatedMetricInterceptorImpl, extra properties: [measured]\n                     Provided by: com.amitinside.aspecio.examples 1.0.0.201911130609 [10]\n\ng! woven\n[0] Service ID: 34, objectClass: [com.amitinside.aspecio.examples.greetings.Hello, com.amitinside.aspecio.examples.greetings.Goodbye]\n    Required Aspects: [com.amitinside.aspecio.examples.aspect.counting.CountingAspect], Optional Aspects: [com.amitinside.aspecio.examples.aspect.metric.MetricAspect$All]\n    Provided by: com.amitinside.aspecio.examples 1.0.0.201911130609 [10]\n    Satisfied: true\n    Active Aspects: [com.amitinside.aspecio.examples.aspect.metric.MetricAspect$All, com.amitinside.aspecio.examples.aspect.counting.CountingAspect]\n\n[1] Service ID: 37, objectClass: [com.amitinside.aspecio.examples.misc.Stuff]\n    Required Aspects: [com.amitinside.aspecio.examples.aspect.metric.Timed], Optional Aspects: []\n    Provided by: com.amitinside.aspecio.examples 1.0.0.201911130609 [10]\n    Satisfied: false\n    Missing Required Aspects: [com.amitinside.aspecio.examples.aspect.metric.Timed]\n\n[2] Service ID: 31, objectClass: [com.amitinside.aspecio.examples.async.SuperSlowService]\n    Required Aspects: [com.amitinside.aspecio.examples.aspect.metric.MetricAspect$AnnotatedOnly], Optional Aspects: [com.amitinside.aspecio.examples.aspect.counting.CountingAspect]\n    Provided by: com.amitinside.aspecio.examples 1.0.0.201911130609 [10]\n    Satisfied: true\n    Active Aspects: [com.amitinside.aspecio.examples.aspect.counting.CountingAspect, com.amitinside.aspecio.examples.aspect.metric.MetricAspect$AnnotatedOnly]\n\ng!\n```\n\n## Project Import\n\n**Import as Eclipse Projects**\n\n1. Install bndtools\n2. Import all the projects (`Eclipse Menu -\u003e File -\u003e Import -\u003e General -\u003e Existing Projects into Workspace`)\n\n\n## Building from Source\n\nRun `./gradlew clean build` in the project root directory.\n\n# Credits\n\nSimon Chemouil has contributed to the development of the [initial version](https://github.com/primeval-io/aspecio). Due to inactivity, this version has been forked by Amit Kumar Mondal, making it OSGi R7 compatible and providing several fixes and enhancements to the source code.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Famitjoy%2Faspecio","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Famitjoy%2Faspecio","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Famitjoy%2Faspecio/lists"}