OSGI LoggerFactory - osgi

In my OSGI endeavours I'm struggeling with another seemling simple problem with logging.
We've included logging to our bundle and it works. We're actually using pax-logging service to do the heavy lifting for us.
import org.ops4j.pax.logging.PaxLoggingService;
import org.osgi.service.component.*;
#Component( immediate=true )
public class ComponentImpl implements TimeService {
#Reference
private PaxLoggingService logs;
#Activate
public void activate(ComponentContext ctx)
{
// deprecated legacy interface
logs.log(PaxLoggingService.LOG_INFO, "Activate called at " + getTime());
logs.log(PaxLoggingService.LOG_INFO, "Activated component " + ctx.getProperties().get("component.id"));
}
}
But two things are bothering us. Firstly, the using the public void log(int level, String message) method directly has been deprecated since OSGI v1.4. Secondly, we'd much rather log through the OSGI LogService.
However this doesn't seem to work just as readily. Our first attempt using the updated logging interface where you first construct a logger instance and then then log through that results in a Java AbstractMethodError:
#Component( immediate=true )
public class ComponentImpl implements TimeService {
#Reference
private PaxLoggingService logs;
#Activate
public void activate(ComponentContext ctx)
{
// fancy, new logging interface - throws exception
Logger logger = logs.getLogger(ComponentImpl.class);
logger.log(PaxLoggingService.LOG_INFO, "Activate called at " + getTime());
}
}
Runtime exception (this also happened when we tried Apache Felix's LoggerService implementation)
java.lang.AbstractMethodError: org.ops4j.pax.logging.service.internal.PaxLoggingServiceImpl$1ManagedPaxLoggingService.getLogger(Ljava/lang/Class;)Lorg/osgi/service/log/Logger;
at com.foobar.baz.osgi.testservice.ComponentImpl.activate(ComponentImpl.java:31)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
...
at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:998)
at aQute.launcher.Launcher.startBundles(Launcher.java:517)
at aQute.launcher.Launcher.activate(Launcher.java:423)
at aQute.launcher.Launcher.run(Launcher.java:301)
at aQute.launcher.Launcher.main(Launcher.java:147)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at aQute.launcher.pre.EmbeddedLauncher.main(EmbeddedLauncher.java:47)
Ideally you wouldn't want to initialize the logger instance in the activate() anyway, but have it setup by the framework. The OSGI spec shows an example of this in section 112.3.12
#Component
public class MyComponent {
#Reference(service=LoggerFactory.class)
private Logger logger;
#Activate
void activate(ComponentContext context) {
logger.trace(“activating component id {}”,
context.getProperties().get(“component.id”));
}
}
Unfortunately, the example doesn't work either. The reference doesn't get resolved and consequently the bundle never runs... I've been searching the web, but haven't found anything pertinent. It seems most people don't use the OSGI service interface for logging and just use slf4j (or another façade) instead; Pax will capture the log entries either way. So technically it makes no difference.
I think our problem is that nobody (neither PAX nor Felix) has implemented the OSGI LoggerFactory interface...
Is there a OSGI LogService implementation out there that does
implement the LoggerFactory?
Is it an advantage to log through an 'implicit' channel (like importing slf4j) instead of using 'explicit' OSGI interfaces?

Currently the best practice for logging in OSGi is to use slf4j as front end.
Logger log = LoggerFactory.getLogger(this.getClass());
Simply use this in your classes. Pax-Logging provides the backend for it and it would also work with a logback backend.
OSGi R7 provides some improved log service integration but I think this is not yet widely available in platforms.
The advantage of using an #Reference for logging is that it eliminates timing issues at startup when maybe your logging backend is not yet available.
The advantage of the slf4j integration like above is that it even works for hybrid jars that also need to work outside OSGi.

By now (Mar '19) there is (at least) Apache Felix Log that fully implements OSGi Logging 1.4.
The easiest way to explicitly use an OSGi Logger that I know of is through this logging facade. Declaring a logger is as simple as with slf4j or log4j and it doesn't require a SCR (or similar).

Related

Dropwizard: dropwizard-guicey sub-resource locators

I'm migrating my application from Jersey2/HK2 to Dropwizard. I'm using dropwizard-guicey to integrate Guice into my application while maintaining some of my HK2 bindings/features.
Dropwizard-guicey binds all resources in singleton scope by default, which leaves me wondering -- what is the suggested way to bind sub-resources in this situation? In Jersey, I can do something as simple as
#Path("{pathParamId}/sub-resource")
public Class<MySubResource> mySubResource() {
return MySubResource.class;
}
But my subresource class then looks like this
public class MySubResource {
private String id;
public MySubResource(#PathParam("pathParamId") id) {
this.id = id;
}
}
It doesn't seem that Dropwizard is able to pick up on the dependencies that Guice binds in MySubResource, since the resources are all managed by Jersey.
What I'm ultimately looking for is the per-request bindings that Jersey gives by default for resources and Guice to do the injection beneath that level.
In your example, root resource will be managed by guice (installed by resource installer) and sub resource will be created by HK (per request).
To use guice services in sub resource you need to enable hk guice bridge:
Add org.glassfish.hk2:guice-bridge:2.5.0-b32 dependency
enable bridge option with .option(GuiceyOptions.UseHkBridge, true).
I add complete demo to the samples repo. It also shows how your case could be implemented with pure guice.

Vaadin with SpringBoot - Redis serialization error

Trying to use SpringSession with Redis storage together with my Vaadin App running under SpringBoot. Session works fine with SpringBoot controllers in my app. But when displaying VaadinUI, even a simple one without any #Autowired beans etc, (example below), I'm getting the following error.
org.springframework.data.redis.serializer.SerializationException: Cannot serialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to serialize object using DefaultSerializer; nested exception is java.io.NotSerializableException: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext
I found similar post here NotSerializableException on serialization of objects currently shown by Vaadin but solution doesnt work for me. I found even this Vaadin ticket https://dev.vaadin.com/ticket/19462 but #SerializableProxy has no efect in the simple UI below as there is no #Autowired bean here and error is still there. Any idea how to resolve this? Database (Redis, memcached etc. ) session storage is very important for load balanced solution we try to build.
#SpringUI(path="/simple")
public class SimpleUI extends UI implements Serializable {
private static final long serialVersionUID = 1L;
#Override
protected void init(VaadinRequest request) {
}
}
It is a known bug. You need to provide your own SpringUiProvider.
https://dev.vaadin.com/ticket/19462

Spring boot AMQP and Spring Hadoop together ends up with missing EmbeddedServletContainerFactory bean

I have two small apps, one uses spring-boot-starter-amqp, other uses spring-data-hadoop-boot. I can run them separately without any problems.
When I join them together, app start fails with exception: org.springframework.context.ApplicationContextException: Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.
My main class is pretty much generic and it works fine for both of them separately:
#PropertySource("file:conf/app.properties")
#SpringBootApplication
public class Job {
public static void main(String[] args) throws Exception {
SpringApplication.run(Job.class, args);
}
}
I am at lost here. AFAIK #SpringBootApplication contains all annotations needed, including auto configuration and components scanning. I've had no need to configure web environment as I am not using it. Why do I need to do it when both dependencies are in class path, and how do I fix it?
UPDATE
I dug a little bit in the Spring Boot code. Main problem is that SpringApplication.deduceWebEnvironment() automatically detects what kind of environment should be configured based on existence of certain classes in class path.
For web environment two classes are being checked. When both of them are in class path, web environment is detected which requires proper configuration, obviously.
javax.servlet.Servlet
org.springframework.web.context.ConfigurableWebApplicationContext
spring-boot-starter-amqp:1.3.1.RELEASE contains ConfigurableWebApplicationContext, and spring-data-hadoop-boot:2.3.0.RELEASE-cdh5 contains Servlet (in native Hadoop libs).
Now, when run alone, one of above classes is missing in both cases, which results in web environment not being set.
But when I use both of them - both classes can be found. Web environment is detected, false positive, and it requires configuration, which I am not able (and don't want) to provide.
So question now is - can I force non web environment, even when I have those classes in class path? Or is there any other way to solve the issue? (other than excluding them from Gradle dependencies)
Solved.
Following this question: How to prevent spring-boot autoconfiguration for spring-web?
I run application as follows.
#PropertySource("file:conf/app.properties")
#SpringBootApplication
public class Job {
public static void main(String[] args) throws Exception {
new SpringApplicationBuilder(Job.class).web(false).run(args);
}
}
Answers to above question also suggested to use property spring.main.web_environment=false or annotation #EnableAutoConfiguration(exclude = WebMvcAutoConfiguration.class). Both solutions haven't worked for me. Only programmatic solution works in my case.

How to use Java-8 default-interface-implementation in a OSGi-Service

I would like to use Java 8 features on the latest Apache-Karaf release (3.0.2) which is supposed to support Java 8.
I have a service-interface within my domain-layer (repository) which has a default-method for identity-generation
public interface MyRepository{
...
default MyId nextIdentity() {
return new MyId(UUID.randomUUID().toString().toUpperCase());
}
}
Then I have a Implementation of that interface which is exposed as a OSGi-Service using Blueprint (Apache-Aries).
When I run my application the bundles get installed successfully, the services get registered, but when the application-layer is calling the method nextIdentity I get a Exception.
IncompatibleClassChangeError: Found interface MyRepository, but class was expected
The application-layer is straight forward: Interface-Attribute which gets its class (in this case OSGi-Service-Reference) injected via Blueprint.
I did check the compilation: all modules are compiled with Java 8 compliance level in Eclipse. I am guessing the problem is related to a aries-proxy which is not Java 8, but since karaf supports it....
EDIT: added Stacktrace
org.apache.wicket.WicketRuntimeException: Can't instantiate page using constructor 'public bikeshop.http.wicket.page.GaragePage()'. Might be it doesn't exist, may be it is not visible (public).
at org.apache.wicket.session.DefaultPageFactory.newPage(DefaultPageFactory.java:193)[92:org.apache.wicket.core:6.7.0]
at org.apache.wicket.session.DefaultPageFactory.newPage(DefaultPageFactory.java:66)[92:org.apache.wicket.core:6.7.0]
at org.ops4j.pax.wicket.internal.PaxWicketPageFactory.newPage(PaxWicketPageFactory.java:76)[100:org.ops4j.pax.wicket.service:3.0.2]
at org.apache.wicket.DefaultMapperContext.newPageInstance(DefaultMapperContext.java:133)[92:org.apache.wicket.core:6.7.0]
at org.apache.wicket.core.request.handler.PageProvider.resolvePageInstance(PageProvider.java:268)[92:org.apache.wicket.core:6.7.0]
at org.apache.wicket.core.request.handler.PageProvider.getPageInstance(PageProvider.java:166)[92:org.apache.wicket.core:6.7.0]
at org.apache.wicket.request.handler.render.PageRenderer.getPage(PageRenderer.java:78)[92:org.apache.wicket.core:6.7.0]
at org.apache.wicket.request.handler.render.WebPageRenderer.respond(WebPageRenderer.java:244)[92:org.apache.wicket.core:6.7.0]
at org.apache.wicket.core.request.handler.RenderPageRequestHandler.respond(RenderPageRequestHandler.java:165)[92:org.apache.wicket.core:6.7.0]
at org.apache.wicket.request.cycle.RequestCycle$HandlerExecutor.respond(RequestCycle.java:854)[92:org.apache.wicket.core:6.7.0]
at org.apache.wicket.request.RequestHandlerStack.execute(RequestHandlerStack.java:64)[91:org.apache.wicket.request:6.7.0]
at org.apache.wicket.request.cycle.RequestCycle.execute(RequestCycle.java:254)[92:org.apache.wicket.core:6.7.0]
at org.apache.wicket.request.cycle.RequestCycle.processRequest(RequestCycle.java:211)[92:org.apache.wicket.core:6.7.0]
at org.apache.wicket.request.cycle.RequestCycle.processRequestAndDetach(RequestCycle.java:282)[92:org.apache.wicket.core:6.7.0]
at org.apache.wicket.protocol.http.WicketFilter.processRequestCycle(WicketFilter.java:259)[92:org.apache.wicket.core:6.7.0]
at org.apache.wicket.protocol.http.WicketFilter$$EnhancerByCGLIB$$91ca4a1b.CGLIB$processRequestCycle$4(<generated>)[92:org.apache.wicket.core:6.7.0]
at org.apache.wicket.protocol.http.WicketFilter$$EnhancerByCGLIB$$91ca4a1b$$FastClassByCGLIB$$36c566fa.invoke(<generated>)[92:org.apache.wicket.core:6.7.0]
at net.sf.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)[99:org.apache.servicemix.bundles.cglib:2.2.2.1]
at org.ops4j.pax.wicket.internal.servlet.PAXWicketServlet$WicketFilterCallback.intercept(PAXWicketServlet.java:150)[100:org.ops4j.pax.wicket.service:3.0.2]
at org.apache.wicket.protocol.http.WicketFilter$$EnhancerByCGLIB$$91ca4a1b.processRequestCycle(<generated>)[92:org.apache.wicket.core:6.7.0]
at org.apache.wicket.protocol.http.WicketFilter.processRequest(WicketFilter.java:201)[92:org.apache.wicket.core:6.7.0]
at org.apache.wicket.protocol.http.WicketFilter$$EnhancerByCGLIB$$91ca4a1b.CGLIB$processRequest$12(<generated>)[92:org.apache.wicket.core:6.7.0]
at org.apache.wicket.protocol.http.WicketFilter$$EnhancerByCGLIB$$91ca4a1b$$FastClassByCGLIB$$36c566fa.invoke(<generated>)[92:org.apache.wicket.core:6.7.0]
at net.sf.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)[99:org.apache.servicemix.bundles.cglib:2.2.2.1]
at org.ops4j.pax.wicket.internal.servlet.PAXWicketServlet$WicketFilterCallback.intercept(PAXWicketServlet.java:150)[100:org.ops4j.pax.wicket.service:3.0.2]
at org.apache.wicket.protocol.http.WicketFilter$$EnhancerByCGLIB$$91ca4a1b.processRequest(<generated>)[92:org.apache.wicket.core:6.7.0]
at org.apache.wicket.protocol.http.WicketFilter.doFilter(WicketFilter.java:282)[92:org.apache.wicket.core:6.7.0]
at org.apache.wicket.protocol.http.WicketFilter$$EnhancerByCGLIB$$91ca4a1b.CGLIB$doFilter$10(<generated>)[92:org.apache.wicket.core:6.7.0]
at org.apache.wicket.protocol.http.WicketFilter$$EnhancerByCGLIB$$91ca4a1b$$FastClassByCGLIB$$36c566fa.invoke(<generated>)[92:org.apache.wicket.core:6.7.0]
at net.sf.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)[99:org.apache.servicemix.bundles.cglib:2.2.2.1]
at org.ops4j.pax.wicket.internal.servlet.PAXWicketServlet$WicketFilterCallback.intercept(PAXWicketServlet.java:150)[100:org.ops4j.pax.wicket.service:3.0.2]
at org.apache.wicket.protocol.http.WicketFilter$$EnhancerByCGLIB$$91ca4a1b.doFilter(<generated>)[92:org.apache.wicket.core:6.7.0]
at org.ops4j.pax.wicket.internal.servlet.PAXWicketServlet.service(PAXWicketServlet.java:98)[100:org.ops4j.pax.wicket.service:3.0.2]
at org.ops4j.pax.wicket.internal.filter.PAXWicketFilterChain.doFilter(PAXWicketFilterChain.java:61)[100:org.ops4j.pax.wicket.service:3.0.2]
at org.ops4j.pax.wicket.internal.filter.FilterDelegator.doFilter(FilterDelegator.java:82)[100:org.ops4j.pax.wicket.service:3.0.2]
at org.ops4j.pax.wicket.internal.servlet.ServletCallInterceptor.service(ServletCallInterceptor.java:168)[100:org.ops4j.pax.wicket.service:3.0.2]
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:684)[71:org.eclipse.jetty.aggregate.jetty-all-server:8.1.15.v20140411]
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:503)[71:org.eclipse.jetty.aggregate.jetty-all-server:8.1.15.v20140411]
at org.ops4j.pax.web.service.jetty.internal.HttpServiceServletHandler.doHandle(HttpServiceServletHandler.java:69)[80:org.ops4j.pax.web.pax-web-jetty:3.1.2]
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:137)[71:org.eclipse.jetty.aggregate.jetty-all-server:8.1.15.v20140411]
at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:557)[71:org.eclipse.jetty.aggregate.jetty-all-server:8.1.15.v20140411]
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:231)[71:org.eclipse.jetty.aggregate.jetty-all-server:8.1.15.v20140411]
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1086)[71:org.eclipse.jetty.aggregate.jetty-all-server:8.1.15.v20140411]
at org.ops4j.pax.web.service.jetty.internal.HttpServiceContext.doHandle(HttpServiceContext.java:240)[80:org.ops4j.pax.web.pax-web-jetty:3.1.2]
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:429)[71:org.eclipse.jetty.aggregate.jetty-all-server:8.1.15.v20140411]
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:193)[71:org.eclipse.jetty.aggregate.jetty-all-server:8.1.15.v20140411]
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1020)[71:org.eclipse.jetty.aggregate.jetty-all-server:8.1.15.v20140411]
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:135)[71:org.eclipse.jetty.aggregate.jetty-all-server:8.1.15.v20140411]
at org.ops4j.pax.web.service.jetty.internal.JettyServerHandlerCollection.handle(JettyServerHandlerCollection.java:77)[80:org.ops4j.pax.web.pax-web-jetty:3.1.2]
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:116)[71:org.eclipse.jetty.aggregate.jetty-all-server:8.1.15.v20140411]
at org.eclipse.jetty.server.Server.handle(Server.java:370)[71:org.eclipse.jetty.aggregate.jetty-all-server:8.1.15.v20140411]
at org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:494)[71:org.eclipse.jetty.aggregate.jetty-all-server:8.1.15.v20140411]
at org.eclipse.jetty.server.AbstractHttpConnection.headerComplete(AbstractHttpConnection.java:971)[71:org.eclipse.jetty.aggregate.jetty-all-server:8.1.15.v20140411]
at org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.headerComplete(AbstractHttpConnection.java:1033)[71:org.eclipse.jetty.aggregate.jetty-all-server:8.1.15.v20140411]
at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:644)[71:org.eclipse.jetty.aggregate.jetty-all-server:8.1.15.v20140411]
at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:235)[71:org.eclipse.jetty.aggregate.jetty-all-server:8.1.15.v20140411]
at org.eclipse.jetty.server.AsyncHttpConnection.handle(AsyncHttpConnection.java:82)[71:org.eclipse.jetty.aggregate.jetty-all-server:8.1.15.v20140411]
at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:696)[71:org.eclipse.jetty.aggregate.jetty-all-server:8.1.15.v20140411]
at org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:53)[71:org.eclipse.jetty.aggregate.jetty-all-server:8.1.15.v20140411]
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608)[71:org.eclipse.jetty.aggregate.jetty-all-server:8.1.15.v20140411]
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:543)[71:org.eclipse.jetty.aggregate.jetty-all-server:8.1.15.v20140411]
at java.lang.Thread.run(Thread.java:745)[:1.8.0_20]
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)[:1.8.0_20]
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)[:1.8.0_20]
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)[:1.8.0_20]
at java.lang.reflect.Constructor.newInstance(Constructor.java:408)[:1.8.0_20]
at org.apache.wicket.session.DefaultPageFactory.newPage(DefaultPageFactory.java:174)[92:org.apache.wicket.core:6.7.0]
... 61 more
Caused by: java.lang.IncompatibleClassChangeError: Found interface bikeshop.domain.repository.BikeRepository, but class was expected
at Proxy04d92f46_988d_4726_9355_6b6381790fde.nextIdentity(Unknown Source)
at bikeshop.application.service.BikeApplicationService.loadGarage(BikeApplicationService.java:22)
at Proxyc25af47a_a344_4a1b_8d0e_429a76d453c6.loadGarage(Unknown Source)
at Proxy163e0a74_12bc_4124_827b_2119133222e8.loadGarage(Unknown Source)
at bikeshop.presentation.internal.GaragePresentationService.init(GaragePresentationService.java:21)
at Proxy5a3bd46a_6830_438c_b5eb_0ca9ec091479.init(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)[:1.8.0_20]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)[:1.8.0_20]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)[:1.8.0_20]
at java.lang.reflect.Method.invoke(Method.java:483)[:1.8.0_20]
at org.ops4j.pax.wicket.util.proxy.LazyInitProxyFactory$JdkHandler.invoke(LazyInitProxyFactory.java:253)
at org.ops4j.pax.wicket.util.proxy.$Proxy22.init(Unknown Source)
at bikeshop.http.wicket.page.GaragePage.<init>(GaragePage.java:30)
The culprit here is probably Blueprint, which generates a proxy class for every imported service, rather than giving you the service object directly. I don't believe that Aries Blueprint has been updated for Java 8 compatibility.
The solution would be to avoid Blueprint and use something like Declarative Services (DS), which is much closer to "real" OSGi Services and gives your consumer the actual service instance. DS definitely works with Java 8 interfaces having default methods.
Update:
This issue seems to be fixed in the proxy-impl 1.1.4 of Aries / Karaf 4.2.3
KARAF-6087
ARIES-1849

Shiro with SAML2 in Karaf with JAX-RS (Jersey)

I am creating an application that runs in Karaf as OSGi container, and uses the OSGi HTTP Service and Jersey for exposing REST APIs. I need to add SAML2 authentication and permissions-based authorization. I would like to use the annotation based approach in Shiro for this, as spring seems to be moving away from OSGi. My questions:
Is Shiro with SAML jars a good fit in OSGi environments?
I want to use WSO2 as the identity provider. Are there any caveats of Shiro and WSO2 working together?
For using annotations, the Shiro docs indicate I need to put AspectJ/Spring/Guice jars - Is this still valid in OSGi environments? I would prefer Guice for all my DI needs.
Would be great to have some insights from Shiro users.
UPDATE
I'm using this project: osgi-jax-rs-connector. So, I use Guice-Peaberry to register OSGi services with the interfaces annotated with #Path or #Provider, and the tool takes care of converting them into a REST resource. (Similar to pax-whiteboard?). I was planning to similarly expose my filters as OSGi services, and then dynamically add them along with the resources.
I have had headaches with AspectJ in OSGi in a previous project where I had to switch to vanilla Equinox from Karaf because the equinox weaving hook was not agreeing with Karaf (stack traces from Aries were seen, among other things). So, would doing something like shiro-jersey be better?
I'm sure it is doable, though I already see some restrictions/issues poping up.
for
1) haven't tried it, though you need to make sure that you tell the pax-web and jetty about it, it'll require adding this to the jetty.xml and it might even need to add a fragment bundle to pax-web-jetty so the desired class can be loaded. This will most likely be your first classnotfound issue.
2) don't know of WSO2 so no idea
3) if you want to use annotations, be careful. For Guice you'll mostlikely will need to use Peaberry since afaik Guice isn't "OSGi-fied" yet. Using AspectJ isn't really a good idea in a OSGi environment due to the classloader restrictions. If you have a compile-time weaving it should be fine, but run-time weaving will be a challange.
UPDATE:
Completely forgot about it, but there is a Pax Shiro Project available, maybe this can be a good starting point to get your setup in a correct lineup.
In the interest of readers, I'm sharing the solution I arrived at after some research of existing tools. First, the easy part: Using Shiro annotations in an OSGi environment. I ended up writing the below class since most Shiro-Jersey adapters shared by developers is based on Jersey 1.x.
#Provider
public class ShiroAnnotationResourceFilter implements ContainerRequestFilter {
private static final Map, AuthorizingAnnotationHandler> ANNOTATION_MAP = new HashMap, AuthorizingAnnotationHandler>();
#Context
private ResourceInfo resourceInfo;
public ShiroAnnotationResourceFilter() {
ANNOTATION_MAP.put(RequiresPermissions.class,
new PermissionAnnotationHandler());
ANNOTATION_MAP.put(RequiresRoles.class, new RoleAnnotationHandler());
ANNOTATION_MAP.put(RequiresUser.class, new UserAnnotationHandler());
ANNOTATION_MAP.put(RequiresGuest.class, new GuestAnnotationHandler());
ANNOTATION_MAP.put(RequiresAuthentication.class,
new AuthenticatedAnnotationHandler());
}
public void filter(ContainerRequestContext context) throws IOException {
Class resourceClass = resourceInfo.getResourceClass();
if (resourceClass != null) {
Annotation annotation = fetchAnnotation(resourceClass
.getAnnotations());
if (annotation != null) {
ANNOTATION_MAP.get(annotation.annotationType())
.assertAuthorized(annotation);
}
}
Method method = resourceInfo.getResourceMethod();
if (method != null) {
Annotation annotation = fetchAnnotation(method.getAnnotations());
if (annotation != null) {
ANNOTATION_MAP.get(annotation.annotationType())
.assertAuthorized(annotation);
}
}
}
private static Annotation fetchAnnotation(Annotation[] annotations) {
for (Annotation annotation : annotations) {
if (ANNOTATION_MAP.keySet().contains(annotation.annotationType())) {
return annotation;
}
}
return null;
}
}
The complete project is here.
The above took care of Part 3 of my question.
For Shiro with SAML, I am using the Servicemix wrapped openSAML jar, and it seems to be working okay till now. I did however had to write a bit of code to make Shiro work with SAML2. It's almost on the same lines as shiro-cas, but is a bit more generic to be used with other IdPs. The code is kind of big so sharing a link to the project instead of copying classes to SO. It can be found here.
Now that I have some abstraction between my code and my IdP, WSO2 integration looks a bit simpler.
P.S. Thanks Achim for your comments and suggestions.

Resources