I have written an API Bundle and some implementing services.
Now i want to use them as plugins, so first of all i need a list of all the services i have.
I'm starting the api like this:
Framework m_fwk = new org.apache.felix.framework.FrameworkFactory().newFramework(null);
m_fwk.init();
AutoProcessor.process(null, m_fwk.getBundleContext());
m_fwk.start();
Bundle api = m_fwk.getBundleContext().installBundle(
"file:/foo/bar/api/target/api-1.0.jar");
api.start();
So now the API is loaded. Now i need to know which bundles implements this API, how can i get this information from the framework?
It sounds like you're trying to re-implement the OSGi service registry. Have a look at Blueprint or Declarative Services instead. At the very least I'd suggest using the OSGi service API to register and consume services.
You only seem to load an API bundle, I guess you want to install other bundles for the implementations? Most people then load a director or so:
for ( File b : bundles.listFiles() ) {
ctx.installBundle( b.toURI().toURL() );
}
Each of these bundle should look like (using DS):
#Component
public class Impl implements API {
public whatever() { ... }
}
The bundle collecting the services could look like:
#Component
public class Collector {
#Reference(type='*')
void addAPI( API api ) { ... }
void removeAPI( API api ) { ... }
}
This is done with the bnd annotations for DS (see bndtools for examples). However, you can also implement/collect the services in Blueprint, iPojo, and many other helpers.
Given that a Framework is also a Bundle, you can get a BundleContext that allows you to find all services you need. You could do something like
m_fwk.getBundleContext().getServiceReferences("com.example.MyInterface", null)
to get all implementers of a given service.
However, you should be aware that you are living in a different classloader than the inhabitants of your framework are.
Related
I am doing the following tutorial at spring.io (https://spring.io/guides/gs/spring-boot/) and this is my first spring-boot application, I am a java-developer. I am using the Maven-approach and deploying it as a stand-alone jar (not relevant to the use-cases really).
The question about the code has to do with structuring and boils down to the question "where should I put in my 'business-logic'?". The logic that I would like to apply concerns the following 2 use-cases.
I want to transform 2 columns in the uploaded csv-file (or excel-file) and let the user download the 'transformed'-file.
If the user uploads an image file, I want to check if there are any EXIF-Tags (such as coordinates ++ ) in the image - and if there is, I would like to store that information in a database.
Or should I take a broader look at Spring MVC or similar technologies when it comes to my use-cases ?
best, Ingo
Right now I am running Ubuntu 18.04 and using java 1.8, maven 3.0.5 and Netbeans 8.2.
To answer your question:
"where should I put in my 'business-logic'?"
You basically want your Controller to handle your requests only and let another class handle the logic, which is a Service in Spring terms.
You would have a file MyService.java similar to this:
#Service
class MyService {
public MyData handleCSV(<your parameters>) {
return ...
}
}
In your Controller you can inject your service and simply use it:
#Controller
class MyController {
#Autowired
private MyService myService;
}
That's it. Anywhere in this controller, you can call the myService instance.
is it Ok if we call wep api service inside web form using api dll. we will be hosting both api and application on same server and requiring internal calling.
It's not very clear what you are after.
First of all if you write an API of some kind then you have to call it to interact with it. There's no middle ground here. If you don't want to call anything then you don't need an API. The purpose of an API is to provide a way to interact with some data storage, so behind your controllers you'd have a layer which talks to a database for example, or even another API.
If you don't want to make any calls then why bother with an API at all? Write a class library, one or several, doing whatever you need them to do and interact with your database this way.
I worked in a project before where I had a somewhat similar situation and ended up writing class libraries which were then shared by a UI project and a WebApi project, so you could work with them either way. This worked quite well actually. If you are looking for something similar then that's what I would go with. Keep the stuff of interest separate so you can expose with an API call or a direct dll reference.
So assuming that your controller methods look something like this:
public interface IService
{
Task<Value> GetValueAsync(int id);
}
public class Service : IService
{
public Task<Value> GetValueAsync(int id)
{
//...
// Code to return a value
//...
}
}
public class ValueController : ApiController
{
private IService _service;
public ValueController(IService service)
{
_service = service
}
public Task<IHttpActionResult> GetValueAsync(int id)
{
return Ok(await _service.GetValueAsync(id));
}
}
Then it is perfectly okay to call the method in the Service class. I would not call the method in the controller as that will cause more problems than you probably want to deal with.
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.
OK. So I have a org.osgi.framework.launch.Framework which I created programmatically in the following way.
framework = ServiceLoader.load(FrameworkFactory.class).iterator().next().newFramework(getFrameworkConfig());
framework.start();
installBundles(BUNDLES_PATH); // installs bundles from a directory, by searching BUNDLES_PATH recursively for JARs
What I want to do, is have a universal loadClass method (as a method in this class) Which will scan the installed bundles, read their Export-Package: declarations, and call the correct bundle's loadClass method, based on the packageName of the class that I'm passing as a parameter.
Is there a smart way to do this? or is it better to just do this:
Class<?> c = null;
// else try every installed bundle one-by-one
for (Bundle bundle : framework.getBundleContext().getBundles()) {
try {
c = bundle.loadClass(className);
} catch (ClassNotFoundException e) {
// OK, move onto next bundle
continue;
}
if (c != null)
break;
}
return c;
I realize I could use services to just have the bundles publish their available services and have the framework query the service with getAllServiceReferences() but this is more work for the programmer, and I'm not sure I wanna go the route of Declarative services.
I would caution against taking this approach. An OSGi framework can contain multiple providers or versions of a package. Trying to make a unified class loader that correctly copes with this is not trivial. However if you want to query the packages being exported you have two options depending on which version of OSGi you are using.
If you are using OSGi prior to OSGi R4.3 you can use PackageAdmin. This is a service registered in the service registry that allows you to query what packages are exported by bundles in the framework. Of specific interest to you will be the getExportedPackage method. This allows you to select a provider of the package and then you can call loadClass on the bundle that provides the package.
PackageAdmin is deprecated in R4.3 (although equinox still implements it). This is more complicated. To do what you desire you need to make use of the new BundleWiring API. This involves getting hold of each bundle in the system and do the following:
BundleWiring bw = bundle.adapt(BundleWiring.class);
List<BundleCapability> capabilities = bw.getCapabilities(BundleRevision.PACKAGE_NAMESPACE);
for (BundleCapability bc : capabilities) {
String pkg = bc.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE);
}
in this you case need to cope with multiple providers and select the correct one. Working out the correct one is the hard part.
I'm trying to create a bundle that watches service registrations and, depending on certain metadata embedded in the API bundle for the service interface, performs some additional tasks. The metadata consists primarily of one or more properties files, so my thought was to use Bundle.findEntries() but since the metadata is embedded in the API bundle, I can't just do something like ServiceReference.getBundle().findEntries() as this would try to find the properties in the service implementation bundle, not in the API bundle.
I thought about getting the service API class name from the ServiceReference ObjectClass property and then using either the Package Admin service or FrameworkUtil.getBundle(), but both of these require a Class--but how do I get the Class of the service interface? The bundle that's doing this work probably hasn't imported the Class's package, so Class.forName() won't work.
My other option is to watch for both bundle and service events: the first creates a registry of bundles that contain the metadata, the second using the first when a service is registered. Before going down that path I'm looking to see if there's an easier way.
Disclaimer: I haven't tried this, but I'm reasonably sure it should do the job.
You can get the packagename from the ServiceReference's ObjectClass, so now we have that, we can find the package in the framework. Given a PackageAdmin packageAdmin, you can do something like
public Bundle getExporterOf(String package, ServiceReference ref) {
ExportedPackage[] packages = packageAdmin.getExportedPackages(packageName);
if (packages == null) {
return null;
}
for (ExportedPackage package : packages) {
Bundle[] importers = package.getImportingBundles()) {
if (importers == null) {
continue;
}
for (Bundle bundle : importers) {
if (bundle.getBundleId() == ref.getBundle().getBundleId()) {
return package.getExportingBundle
}
}
}
}
What we're doing here, is find all packages with the given package name (there might be multiple), find the one that the bundle that registered the service imports, and get the bundle that exported that package. You can probably make the method a little nicer.