Osgi REST API service using ECF and bndtools is not accessible - rest
I'm currently trying to expose restful webservice using OSGI remote services, but it seems that i'm missing something from ECF tutorial.
below the details here is the impl class:
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import org.example.api.HelloWorldService;
import org.osgi.service.component.annotations.Component;
#Path("/helloworld")
#Component(property = { "service.exported.interfaces=HTTP",
"service.exported.configs=ecf.jaxrs.jersey.server",
"ecf.jaxrs.jersey.server.alias=/jersey" })
public class HelloWorldResource implements HelloWorldService {
#GET
#Produces("text/plain")
public String getMessage() {
// Return some textual content
return "Hello World";
}}
I followed this link to know how things can be together but i wasn't able to get HTTP 200 ok message based on the jax-rs path annotation
https://wiki.eclipse.org/Tutorial:_Using_REST_and_OSGi_Standards_for_Micro_Services
Please note that all bundle are resolved correctly.
I've created a bndtools4 bndrun for something like your hello jaxrs service. First, you need to create a new workspace using the ECF bndtools.workspace template. Just follow the instructions here:
https://wiki.eclipse.org/Bndtools_Support_for_Remote_Services_Development
for creating a workspace from the ECF bndtools.workspace template. Just today I've added the JaxRS bundles to this workspace template.
Then I created an org.example.api project with this interface
package org.example.api;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
#Path("/helloworld")
public interface HelloWorldService {
#GET
#Path("/hello")
#Produces("text/plain")
String getMessage();
}
Note that it differs slightly from yours because it has a #PATH annotation for the getMessage() method.
Then in another project...named org.example.impl there is this class:
package org.example.impl;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import org.example.api.HelloWorldService;
import org.osgi.service.component.annotations.Component;
#Path("/helloworld")
#Component(property = { "service.exported.interfaces=*",
"service.intents=jaxrs" })
public class HelloWorldResource implements HelloWorldService {
#GET
#Path("/hello")
#Produces("text/plain")
public String getMessage() {
// Return some textual content
return "Hello World";
}}
The jaxrs annotations are the same as with the interface (as they should be). There are fewer
And here's the bndrun (which I've called jettyserver.bndrun):
-runrequires: \
bnd.identity;id='org.eclipse.ecf.provider.jersey.server',\
osgi.identity;filter:='(&(osgi.identity=javax.validation.api)(version>=1.1.0))',\
osgi.identity;filter:='(&(osgi.identity=org.apache.felix.gogo.command)(version>=1.0.2))',\
osgi.identity;filter:='(&(osgi.identity=org.apache.felix.gogo.runtime)(version>=1.0.10))',\
osgi.identity;filter:='(&(osgi.identity=org.slf4j.api)(version>=1.7.2))',\
bnd.identity;version='latest';id='org.example.impl',\
bnd.identity;id='org.apache.felix.gogo.command',\
bnd.identity;id='org.apache.felix.gogo.runtime',\
bnd.identity;id='org.apache.felix.gogo.shell',\
bnd.identity;id='org.eclipse.ecf.osgi.services.remoteserviceadmin.console',\
bnd.identity;id='org.apache.felix.scr',\
bnd.identity;id='org.eclipse.ecf.osgi.services.distribution',\
bnd.identity;id='org.eclipse.ecf.provider.jersey.client'
-runfw: org.apache.felix.framework;version='[5.6.10,5.6.10]'
-runee: JavaSE-1.8
-runbundles: \
com.fasterxml.jackson.core.jackson-annotations;version='[2.9.2,2.9.3)',\
com.fasterxml.jackson.core.jackson-core;version='[2.9.2,2.9.3)',\
com.fasterxml.jackson.core.jackson-databind;version='[2.9.2,2.9.3)',\
com.fasterxml.jackson.jaxrs.jackson-jaxrs-base;version='[2.9.2,2.9.3)',\
com.fasterxml.jackson.jaxrs.jackson-jaxrs-json-provider;version='[2.9.2,2.9.3)',\
javax.annotation;version='[1.2.0,1.2.1)',\
javax.inject;version='[1.0.0,1.0.1)',\
javax.persistence;version='[2.2.0,2.2.1)',\
javax.validation.api;version='[1.1.0,1.1.1)',\
javax.ws.rs;version='[2.0.1,2.0.2)',\
org.aopalliance;version='[1.0.0,1.0.1)',\
org.apache.felix.configadmin;version='[1.8.16,1.8.17)',\
org.apache.felix.eventadmin;version='[1.4.10,1.4.11)',\
org.apache.felix.gogo.command;version='[1.0.2,1.0.3)',\
org.apache.felix.gogo.runtime;version='[1.0.10,1.0.11)',\
org.apache.felix.http.jetty;version='[3.4.8,3.4.9)',\
org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\
org.apache.felix.scr;version='[2.0.14,2.0.15)',\
org.eclipse.core.jobs;version='[3.9.3,3.9.4)',\
org.eclipse.ecf;version='[3.9.0,3.9.1)',\
org.eclipse.ecf.discovery;version='[5.0.300,5.0.301)',\
org.eclipse.ecf.identity;version='[3.9.0,3.9.1)',\
org.eclipse.ecf.osgi.services.remoteserviceadmin;version='[4.6.800,4.6.801)',\
org.eclipse.ecf.osgi.services.remoteserviceadmin.proxy;version='[1.0.100,1.0.101)',\
org.eclipse.ecf.provider.jaxrs;version='[1.3.0,1.3.1)',\
org.eclipse.ecf.provider.jaxrs.server;version='[1.4.0,1.4.1)',\
org.eclipse.ecf.provider.jersey.server;version='[1.3.0,1.3.1)',\
org.eclipse.ecf.remoteservice;version='[8.13.0,8.13.1)',\
org.eclipse.ecf.remoteservice.asyncproxy;version='[2.1.0,2.1.1)',\
org.eclipse.equinox.common;version='[3.9.0,3.9.1)',\
org.eclipse.equinox.concurrent;version='[1.1.0,1.1.1)',\
org.eclipse.equinox.supplement;version='[1.7.0,1.7.1)',\
org.eclipse.osgi.services.remoteserviceadmin;version='[1.6.200,1.6.201)',\
org.glassfish.hk2.api;version='[2.5.0,2.5.1)',\
org.glassfish.hk2.osgi-resource-locator;version='[2.5.0,2.5.1)',\
org.glassfish.hk2.utils;version='[2.5.0,2.5.1)',\
org.glassfish.jersey.bundles.repackaged.jersey-guava;version='[2.22.1,2.22.2)',\
org.glassfish.jersey.containers.servlet.core;version='[2.22.1,2.22.2)',\
org.glassfish.jersey.core.jersey-common;version='[2.22.1,2.22.2)',\
org.glassfish.jersey.core.jersey-server;version='[2.22.1,2.22.2)',\
org.glassfish.jersey.ext.entityfiltering;version='[2.22.1,2.22.2)',\
org.glassfish.jersey.media.jersey-media-json-jackson;version='[2.22.1,2.22.2)',\
org.slf4j.api;version='[1.7.2,1.7.3)',\
org.example.api;version=snapshot,\
org.example.impl;version=snapshot,\
org.apache.felix.gogo.shell;version='[1.0.0,1.0.1)',\
org.eclipse.ecf.console;version='[1.1.0,1.1.1)',\
org.eclipse.ecf.osgi.services.remoteserviceadmin.console;version='[1.0.0,1.0.1)',\
org.eclipse.ecf.osgi.services.distribution;version='[2.1.200,2.1.201)',\
javassist;version='[3.13.0,3.13.1)',\
org.eclipse.ecf.provider.jaxrs.client;version='[1.3.0,1.3.1)',\
org.eclipse.ecf.provider.jersey.client;version='[1.3.0,1.3.1)',\
org.glassfish.hk2.locator;version='[2.5.0,2.5.1)',\
org.glassfish.jersey.core.jersey-client;version='[2.22.1,2.22.2)'
With your permission, I would like to add bndtools project templates for both jaxrs hello api and impl projects to the ECF bndtools.workspace repo, along with a jersey and cxf bndrun server and client templates (once I complete them). I'll create the bndruns and test over the next few days.
The exported interfaces property should not be "HTTP". Instead it must be an interface fqname or Star. So try this:
"service.exported.interfaces=*"
If you are not using already, please update to ECF 3.14.0. Further, after 3.14.0/Photon (last few weeks) there have been significant changes to the JaxRS providers to support OSGi R7 Async Remote Services so please make sure you have the latest from JaxRSProviders as well. The required remote service properties have changed based upon the R7 changes. Please see the service properties of the hello examples and this short tutorial for running the hello example on Karaf:
https://wiki.eclipse.org/Tutorial:_JaxRS_Remote_Services_on_Karaf
In short, it's no longer necessary to specify ecf.jaxrs.jersey.server.alias but there are other (newly standardize) properties to set.
I would like to create a bndtools4 JaxRS run descriptor template, and I think this would make it significantly easier for you. I suspect your problem might be that do not have all necessary jersey bundles, as jersey has quite a number of dependencies...all of which have to be present to successfully export a remote service. For ref, here's the setup info for using the project and bndrun templates that are currently there:
https://wiki.eclipse.org/Bndtools_Support_for_Remote_Services_Development
I will create a bndtools4 bndrun template for the CXF and Jersey distribution providers, but have not had enough time since the JaxRSProvider changes. If you would like to express your desire for bndrun template and/or help with contributions, please open an issue at https://github.com/ECF/JaxRSProviders/issues and that will help track.
In the mean time, the examples list of bundles is in the product files here:
https://github.com/ECF/JaxRSProviders/tree/master/examples/com.mycorp.examples.student.remoteservice.host/launch
For info:
I've added project templates for JaxRS API, Impl, Consumer projects (based upon the HelloWorldService) and I've added bndrun templates for Jersey server and client, as well as CXF server and client.
See https://github.com/ECF/JaxRSProviders/issues/6
Related
Does SBT to support preemptive auth for downloading packages?
I am running SBT 1.2.8 and my project needs to download packages from a repo on a privately hosted Artifactory instance. My repo is protected by basic auth. After reading a multitude of examples and instructions, I created a credentials.properties file in my repo. realm=Artifactory Realm host=artifactory.mycompany.com username=my_username password=my_password I then added the following to my build.sbt file credentials += Credentials(new File("credentials.properties")) Then I added the repository to my list of resolvers in resolvers.sbt "My Company Artifactory" at "https://artifactory.mycompany.com/artifactory/my_private_repo/", I built my application and was able to download the protected packages just fine. However, a system administrator at my company requested I turn on the “Hide Existence of Unauthorized Resources” setting in Artifactory. This setting forces Artifactory to return 404 errors when an unauthenticated user tries to access protected resources. Usually in this case, Artifactory returns 401s with a WWW-Authenticate header. Suddenly, my application was unable to resolve its dependencies. I turned the Artifactory setting off and then back on again and verified this setting was, in fact, the cause of my problems. It appears as though SBT will not send credentials unless it is challenged with a 401 and a WWW-Authenticate header (with the proper realm). Looking at the docs and GitHub issues for SBT, Ivy, and Coursier, it seems this “preemptive authentication” is not a supported feature. I spend many hours trying to resolve this in various ways, but I cannot find a solution. Here is what I have tried: Adding my Artifactory username and password to the repository url, so it looks like https://my_username:my_password#artifactory.mycompany.com/artifactory/my_private_repo/. This worked in my browser and a REST client, but not with SBT. Omitting the “realm” from my credentials file Switching to SBT 1.3.9 and trying everything above with the new version. Does anyone know how I can get SBT to use preemptive HTTP basic auth? It appears both Maven and Gradle support this (see links below), but I cannot find anything in the SBT docs. Maven support for preemptive auth: https://jfrog.com/knowledge-base/why-does-my-maven-builds-are-failing-with-a-404-error-when-hide-existence-of-unauthorized-resources-is-enabled/ Gradle support for preemptive auth: https://github.com/gradle/gradle/pull/386/files I'm almost thinking of setting up a local proxy to send the proper headers Artifactory, and point SBT to use the local proxy as a resolver. However, that seems needlessly cumbersome for developers to use.
you are correct. You can setup an AbstractRepository. See https://github.com/SupraFii/sbt-google-artifact-registry/blob/master/src/main/scala/ch/firsts/sbt/gar/ArtifactRegistryRepository.scala#L21 for example: package ch.firsts.sbt.gar import java.io.File import java.util import com.google.cloud.artifactregistry.wagon.ArtifactRegistryWagon import org.apache.ivy.core.module.descriptor.Artifact import org.apache.ivy.plugins.repository.AbstractRepository import org.apache.maven.wagon.repository.Repository class ArtifactRegistryRepository(repositoryUrl: String) extends AbstractRepository { val repo = new Repository("google-artifact-registry", repositoryUrl) val wagon = new ArtifactRegistryWagon() override def getResource(source: String): ArtifactRegistryResource = { val plainSource = stripRepository(source) wagon.connect(repo) ArtifactRegistryResource(repositoryUrl, plainSource, wagon.resourceExists(plainSource)) } override def get(source: String, destination: File): Unit = { val adjustedSource = if (destination.toString.endsWith("sha1")) source + ".sha1" else if (destination.toString.endsWith("md5")) source + ".md5" else source wagon.connect(repo) wagon.get(adjustedSource, destination) } override def list(parent: String): util.List[String] = sys.error("Listing repository contents is not supported") override def put(artifact: Artifact, source: File, destination: String, overwrite: Boolean): Unit = { val plainDestination = stripRepository(destination) wagon.connect(repo) wagon.put(source, plainDestination) } private def stripRepository(fullName: String): String = fullName.substring(repositoryUrl.length + 1) }
Camel Restlet - Context Path Ambiquity
I have Spring Boot Camel application where rest apis are exposed using camel-restlet Sample route #Component public class AppRoute extends RouteBuilder{ public void configure(CamelContext context){ from("restlet:employee?restletMethods=GET").log("${body}"); } } The App runs perfect ( spring-boot:run ). but am unable to locate under which path the API is exposed. Log has no information. Every API i hit returns 404. Log shows the route has been started. Under which path is it running. And how do I change it? Note: Please dont suggest any XML based configuration. Anything that I can put under #Configuration would be perfect
I would go with the Rest DSL which is supported by the camel-restlet component like this restConfiguration().component("restlet").port(8080); rest("/rest") .get("employee") .route().log("${body}") .endRest(); And this route will listen to the following url http://localhost:8080/rest/employee EDIT: I guess you could do something like without using the Rest DSL String host = InetAddress.getLocalHost().getHostName(); from("restlet:http://" + host + contextPath + "/employee?restletMethods=GET").log("${body}") The port and context path are configurable with the following properties camel.component.restlet.port=8686 server.servlet.context-path=/my-path The context path can be injected in the routeBuilder with #Value("${server.servlet.context-path}") private String contextPath;
According to the documentation, the format of the URI in a restlet endpoint definition should be the following: restlet:restletUrl[?options] Where restletUrl should have the following format: protocol://hostname[:port][/resourcePattern] So in your case you could define the URI in the following way: from("restlet:http://localhost/employee?restletMethods=GET") This should make the endpoint available under the following URL: http://localhost/employee Which you can test e.g. in a web browser.
Use the first of the three configuration methods described here: https://restlet.com/open-source/documentation/javadocs/2.0/jee/ext/org/restlet/ext/servlet/ServerServlet.html You should be able to customize it using the Component: https://restlet.com/open-source/documentation/javadocs/2.0/jee/api/org/restlet/Component.html?is-external=true See in particular setServers() methods (or XML equivalent) to change the hostname and port.
How do you get logging from the CXF Rest Client?
This took me quite a long time to figure out. I'm asking this question so I can answer it for others: How do you get useful logging info from the CXF Rest Client? EG: The url, params, payload, response, etc. Note: This question already exists but it's asking about CXF and Resteasy. I only want the answer for CXF: Logging in CXF and RestEasy clients
Here's how you do it with CXF: import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider; import org.apache.cxf.interceptor.LoggingInInterceptor; import org.apache.cxf.interceptor.LoggingOutInterceptor; import org.apache.cxf.jaxrs.client.ClientConfiguration; import org.apache.cxf.jaxrs.client.WebClient; import org.json.JSONException; import org.json.JSONObject; ... WebClient client = WebClient.create(endPoint, providers).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_JSON); ClientConfiguration config = WebClient.getConfig(client); config.getInInterceptors().add(new LoggingInInterceptor()); config.getOutInterceptors().add(new LoggingOutInterceptor());
Using the JAX-RS 2.0 client from CXF 3.1.x I am having some trouble getting the LoggingInInterceptor and LoggingOutInterceptor to work. I changed to use the LoggingFeature as described at http://cxf.apache.org/docs/message-logging.html and it worked first try: Client client = ClientBuilder.newBuilder().register(LoggingFeature.class).build()
You can pass directly an instance of LoggingFeature to your client using the appropriate create() method. Response response = WebClient .create(baseAddress, Arrays.<Object>asList(Arrays .<Object>asList(new JacksonJsonProvider())), Arrays.<Feature>asList(new LoggingFeature()), null) .type(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON).path(path).post(request);
Determine web app path
I am using Spring Tool Suite (really eclipse). I just created a new springMVC project and created a simple controller. There was a problem with how STS created the project so I had to manually fix the groupID and artifactID in the pom. The problem I am currently having is I can't seem to hit my tomcat server (published and launched by STS). I have checked the directory structure in tomcat where it gets published and everything seems to be fine but I get 404's when I try and hit the controller. The tomcat logs look as if nothing has even tried to connect to it. They also show that my controller has been mapped: 2013-10-14 09:09:17.763] INFO o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped "{[/Login],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public java.lang.String com.verisk.underwriting.ims.web.IMSController.test() This is what my controller looks like: #Controller #RequestMapping("Login") public class IMSController { #RequestMapping(value = "", method = RequestMethod.GET) #ResponseBody public String test() { return "SUCCESS"; } } The app is called ims, so I should be able to hit this controller with this request: http://localhost/ims/Login It is configured with a java config (AppConfig.java): #Configuration #EnableWebMvc #ComponentScan(basePackages = "com.some.package.ims.web") public class AppConfig extends WebMvcConfigurerAdapter { #Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/resources/**").addResourceLocations("/resources/"); } } Is there a config file that specifies the base path for the app?
Have a look at the .metadata directory of our workspace. It has a .plugins folder, that constains the org.eclipse.wst.server.core directory, and there is one (or more) tmp0 diectory. That contains a wtpwebapps directory. This contains the deployed webapps with the name that is used -- for example MyApp. <Workspace>\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\MyApp Then your Login page is located at http://localhost[:8080]/MyApp/Login
http://localhost/ims/Login will hit port 80 ; by default tomcat runs on port 8080. So unless you have changed tomcat's HTTP port to 80 you need to use localhost:8080 If the port is good then check that your application context path is really ims, by default it is the exact name of the generated WAR file. If you use WTP, eclipse "servers" view will show it under the server instance. If the context path is good then check the configured URL mapping in your web.xml descriptor. Make sure you are not missing a prefix in the URL for REST/MVC servlet URL aggregation. In your case it looks like you should use .../resources/Login because your configured your resources to be under the /resources/** pattern.
scala cloud foundry mongodb : access to mongodb denied
I have installed eclipse, the cloudfoundry plugin, the scala plugin,the vaadin plugin(for web developments) and the mongodb libraries. I created a class like this : import vaadin.scala.Application import vaadin.scala.VerticalLayout import com.mongodb.casbah.MongoConnection import com.mongodb.casbah.commons.MongoDBObject import vaadin.scala.Label import vaadin.scala.Button class Launcher extends Application { val label=new Label override def main = new VerticalLayout() { val coll=MongoConnection()("mybd")("somecollection") val builder=MongoDBObject.newBuilder builder+="foo1" -> "bar" var newobj=builder.result() coll.save(newobj) val mongoColl=MongoConnection()("mybd")("somecollection") val withFoo=mongoColl.findOne() label.value=withFoo add(label) //bouton pour faire joli add(new Button{ caption_=("click me!") }) } } the error (the access to the mongodb database is denied) comes from the parameters, which are the default ones. do you know how to set up the good parameters in scala or in java?
Looks like you got some help on the vcap-dev mailing list package com.example.vaadin_1 import vaadin.scala.Application import org.cloudfoundry.runtime.env.CloudEnvironment import org.cloudfoundry.runtime.env.MongoServiceInfo import com.mongodb.casbah.MongoConnection class Launcher extends Application { val cloudEnvironment = new CloudEnvironment() val mongoServices = cloudEnvironment.getServiceInfos(classOf[MongoServiceInfo]) val mongo = mongoServices.get(0) val mongodb = MongoConnection(mongo.getHost(), mongo.getPort())("abc") mongodb.authenticate(mongo.getUserName(),mongo.getPassword()) }
I would suggest to do it using Spring Data for MongoDB there is a sample application for Cloudfoundry in particular put together by the Spring guys. With a bit of xml configuration you have ready to inject the mongoTemplate similar to the familiar Spring xxxTemplate paradigm.
when deploying to CloudFoundry, the information relative to connecting to a service (i.e mongo in your case) is made available to the app through environment variable VCAP_SERVICES. It is a json document with one entry per service. You could of course parse it yourself, but you will find the class http://cf-runtime-api.cloudfoundry.com/org/cloudfoundry/runtime/env/CloudEnvironment.html useful. You will need to add the org.cloudfoundry/cloudfoundry-runtime/0.8.1 jar to your project. You can use it without Spring. Have a look at http://docs.cloudfoundry.com/services.html for explanation of the VCAP_SERVICES underlying var