Micronaut - Create a bean without creating ApplicationContext - java

I have a Micronaut declarative HTTP client written using #client annotation. I want to call this while starting micronaut app before creating the ApplicationContext itslef.
HttpClient : SampleHttpClient.java
import io.micronaut.http.HttpResponse;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Header;
import io.micronaut.http.client.annotation.Client;
#Client("http://127.0.0.1:8200")
#Header(name = "X-Vault-Token", value = "hvs.CEGT7cKyMA8wsDbgKZqxC34q")
public interface SampleHttpClient {
#Get(value = "/v1/kv/data/KMS", produces = MediaType.APPLICATION_JSON)
HttpResponse<String> getVaultSecret();
}
Application.java (Main class)
import io.micronaut.context.ApplicationContext;
import io.micronaut.runtime.Micronaut;
public class Application {
public static void main(String[] args) {
// Following code works perfect. I am creating context here. But I dont want to do this
SampleHttpClient client = Micronaut.run(Application.class, args).
getBeansOfType(SampleHttpClient.class).stream().findFirst().get();
System.out.println("Response Body ="+client.getVaultSecret().body());
// How do we get the instance of SampleHttpClient without using Micronaut's dependency
injection process???
}
}

I want to call this while starting micronaut app before creating the
ApplicationContext itslef.
Micronaut doesn't provide a mechanism to support that. You could write your own thing that instantiates the beans, but that is a large undertaking. You would be writing your own bean container.

Related

Do a HTTP POST instead of a GET

The WebService I'm trying to use is SOAP and receives only the HTTP method POST on its requests, but for some reason, I'm not being able to make that happen and it's sending only GET.
I've been loosely following this guide: https://spring.io/guides/gs/consuming-web-service/
I've thought that it wouldn’t be the case of the wrong method, but I've tried via Postman with the logged envelope and it worked fine, also this:
debugger image with GET
This is how I've done it:
package com.example.ws;
import com.demo.partner.wsdl.ObjectFactory;
import com.demo.partner.wsdl.StoreInformation;
import com.example.util.PartnerWSHttpHeaderCallBack;
import com.example.util.SoapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ws.client.core.support.WebServiceGatewaySupport;
public class StoreInformationClient extends WebServiceGatewaySupport {
private static final Logger log = LoggerFactory.getLogger(StoreInformationClient.class);
#Autowired
private ObjectFactory objectFactory;
public StoreInformation getStoreInformation() {
StoreInformation storeInformation = objectFactory.createStoreInformation();
storeInformation.setStoreID("99633");
return (StoreInformation) getWebServiceTemplate()
.marshalSendAndReceive("https://aurl.com/api/ws.wsdl", SoapUtils.buildEnvelope(storeInformation, StoreInformation.class),
new PartnerWSHttpHeaderCallBack());
}
}
package com.example.configuration;
import com.demo.partner.wsdl.ObjectFactory;
import com.example.ws.StoreInformationClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
#Configuration
public class AppConfiguration {
#Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("com.demo.partner.wsdl");
return marshaller;
}
#Bean
public StoreInformationClient countryClient(Jaxb2Marshaller marshaller) {
StoreInformationClient client = new StoreInformationClient();
client.setMarshaller(marshaller);
client.setUnmarshaller(marshaller);
return client;
}
#Bean
public ObjectFactory objectFactory() {
return new ObjectFactory();
}
}
package com.example.util;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.client.core.WebServiceMessageCallback;
import org.springframework.ws.transport.WebServiceConnection;
import org.springframework.ws.transport.context.TransportContext;
import org.springframework.ws.transport.context.TransportContextHolder;
import org.springframework.ws.transport.http.HttpUrlConnection;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class PartnerWSHttpHeaderCallBack implements WebServiceMessageCallback {
#Override
public void doWithMessage(WebServiceMessage webServiceMessage) {
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "Basic definitelyABase64value");
headers.put("SOAPAction", "http://www.partner.com/action/GetStoreInformation");
headers.put("Content-Type", "text/xml");
addRequestHeader(headers);
}
private void addRequestHeader(Map<String, String> headers) {
TransportContext context = TransportContextHolder.getTransportContext();
WebServiceConnection connection = context.getConnection();
if (connection instanceof HttpUrlConnection) {
HttpUrlConnection conn = (HttpUrlConnection) connection;
for (Map.Entry<String, String> entry : headers.entrySet()) {
String k = entry.getKey();
String v = entry.getValue();
try {
conn.addRequestHeader(k, v);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
Had a look at both 6 api and 8 api docs for spring and find that way is almost impossible (if not it is impossible) because as you are finding there does not appear to be a method in that hierarchy set of classes and interfaces (IN YOUR FORM OF CODE) a sensible coherent way of converting the http connection to "POST" , it will only send "GET".
I do understand that the HttpUrlConnection is not java.net and is an org.spring. but the spring connections must be obtained through extensions and the instance context of the connected objects.
the spring framework documentation seems erroneous also because it shows no-where org.springframework.ws.transport.TransportConstants and org.springframework.ws.transport.http.HttpTransportConstants are implemented.
Niether taking a look searching the web.
All i can suggest "trying" is this however "Spring.org project" needs to clearly document how to set the constants for those interfaces AND if anything actually implements them !
/*
org.springframework.ws.transport.TransportConstants
import org.springframework.ws.transport.http.HttpTransportConstants
*/
HttpTransportConstants httCxt = ((HttpTransportConstants)TransportContextHolder.getTransportContext());
// perhaps httCxt."METHOD_POST";
httCxt.METHOD_POST;
// no really, there should be an implementing class in the .transport packages that uses and sets this however i could not locate one throughout the ws classes and interfaces.
// all interface variables are static and final (because interfaces are abstract they can be cast together)
TransportContext context = (TransportContext)httCxt;
Here is an article that seems to have some way alike
https://pretagteam.com/question/soap-request-to-webservice-with-java
however i think they use a java.net HttpURLConnection
There is a similar HttpURLConnection object in spring
*** The spring docs also do not show full hierarchy on identical classes and interfaces**
org.springframework.ws.transport.http
Class HttpUrlConnection IMPLEMENTS org.springframework.ws.transport
Interface WebServiceConnection
The following worked many years back for applets and since finding ambiguous docs naming it appears this would work (or be immensely close)
To make a org.springframework.ws.transport.http.HttpUrlConnection
make a protected method
protected org.springframework.ws.transport.http.HttpUrlConnection makeConnHttp(String uuu){
java.net.URL urli = new java.net.URL(uuu);
Java.net.UrlConnection urlcon = new Java.net.UrlConnection(urli);
Java.net.HttpUrlConnection netUrl = (Java.net.HttpUrlConnection)urlcon;
netUrl.setRequestMethod("POST");
org.springframework.ws.transport.http.HttpUrlConnection wsUrlConn = new org.springframework.ws.transport.http.HttpUrlConnection(netUrl);
return (org.springframework.ws.transport.http.HttpUrlConnection)wsUrlConn;
}
public org.springframework.ws.transport.http.HttpUrlConnection getWSconnectionHttp(String uuu){
return (org.springframework.ws.transport.http.HttpUrlConnection)makeConnHttp(uuu);
}
// spring 2.1.4 HttpUrlConnection is also a WebServiceConnection
org.springframework.ws.transport.WebServiceConnection wdConnsvc = (org.springframework.ws.transport.WebServiceConnection)getWSconnectionHttp(uuu);
It is not known to me whether the api process calls open()/close() on UrLConnection or that has to be done manually.
Where your code goes wrong
Notice how your code gets a TransportContext and it gets a WebServiceConnection.
That produced object instance is a SEPERATE object instance not related to the later constructed spring framework HttpUrlConnection.
You could open the connection but it is not the WebServiceConnection object constructed with the transport context.
The problem is the org spring HttpUrlConnection IS( a WebServiceConnection) also by polymorphism , so you do not need to obtain a WebServiceConnection because you would have that from creating a org.spring HttpUrlConnection.
However an org spring HttpUrlConnection must be fed an implementing coded java.net.HttpUrlConnection for the actual networking, the org spring HttpUrlConnection is more of a joiner wrapper for obtaining and managing data bound to the java.net.HttpUrlConnection and the containers registrations of the service endpoints.
Your code would require joining the two parts by replacing any instance of org.springframework....HttpUrlConnection with an/or-the externally created HttpUrlConnection using lava.lang.reflect.
However, the best method for binding objectes in that problem is extend from the class org.springframework.ws.transport.http.HttpUrlConnection to prevent binding and access problems.

How to start multiple message consumers in Quarkus?

I'm trying to migrate from Vert.x to Quarkus and in Vert.x when I write message consumers like Kafka/AMQP etc. I have to scale the number of verticals to maximize performance across multiple cores i.e. Vertical Scaling - is this possible in Quarkus? I see a similar question here but it wasn't answered.
For example, with Kafka I might create a consumer inside a vertical and then scale that vertical say 10 times (that is specify the number of instances in the deployment to be 10) after doing performance testing to determine that's the optimal number. My understanding is that by default, 1 vertical = 1 event loop and does not scale across multiple cores.
I know that it's possible to use Vert.x verticals in Quarkus but is there another way to scale things like the number of Kafka consumers across multiple core?
I see that this type of scalability is configurable for things like Quarkus HTTP but I can't find anything about message consumers.
Here's the Vert.x Verticle approach that overall I'm very happy with, but I wish there were better documentation on how to do this.
UPDATE - Field injection doesn't work with this example but constructor injection does work.
Lets say I want to inject this
#ApplicationScoped
public class CoffeeRepositoryService {
public CoffeeRepositoryService() {
System.out.println("Injection succeeded!");
}
}
Here's my Verticle
package org.acme;
import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.vertx.core.AbstractVerticle;
import io.vertx.core.impl.logging.Logger;
import io.vertx.core.impl.logging.LoggerFactory;
import io.vertx.mutiny.core.eventbus.EventBus;
import io.vertx.mutiny.rabbitmq.RabbitMQClient;
import io.vertx.mutiny.rabbitmq.RabbitMQConsumer;
import io.vertx.rabbitmq.QueueOptions;
import io.vertx.rabbitmq.RabbitMQOptions;
public class RQVerticle extends AbstractVerticle {
private final Logger LOGGER = LoggerFactory.getLogger(org.acme.RQVerticle.class);
//This doesn't work - returns null
#Inject
CoffeeRepositoryService coffeeRepositoryService;
RQVerticle() {} // dummy constructor needed
#Inject // constructor injection - this does work
RQVerticle(CoffeeRepositoryService coffeeRepositoryService) {
//Here coffeeRepositoryService is injected properly
}
#Override
public Uni<Void> asyncStart() {
LOGGER.info(
"Creating RabbitMQ Connection after Quarkus successful initialization");
RabbitMQOptions config = new RabbitMQOptions();
config.setUri("amqp://localhost:5672");
RabbitMQClient client = RabbitMQClient.create(vertx, config);
Uni<Void> clientResp = client.start();
clientResp.subscribe()
.with(asyncResult -> {
LOGGER.info("RabbitMQ successfully connected!");
});
return clientResp;
}
}
Main Class - injection doesn't work like this
package org.acme;
import io.quarkus.runtime.Quarkus;
import io.quarkus.runtime.QuarkusApplication;
import io.quarkus.runtime.annotations.QuarkusMain;
import io.vertx.core.DeploymentOptions;
import io.vertx.mutiny.core.Vertx;
#QuarkusMain
public class Main {
public static void main(String... args) {
Quarkus.run(MyApp.class, args);
}
public static class MyApp implements QuarkusApplication {
#Override
public int run(String... args) throws Exception {
var vertx = Vertx.vertx();
System.out.println("Deployment Starting");
DeploymentOptions options = new DeploymentOptions()
.setInstances(2);
vertx.deployVerticleAndAwait(RQVerticle::new, options);
System.out.println("Deployment completed");
Quarkus.waitForExit();
return 0;
}
}
}
Main Class with working injection but cannot deploy more than one instance
package org.acme;
import io.quarkus.runtime.StartupEvent;
import io.vertx.mutiny.core.Vertx;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import org.jboss.logging.Logger;
#ApplicationScoped
public class MainVerticles {
private static final Logger LOGGER = Logger.getLogger(MainVerticles.class);
public void init(#Observes StartupEvent e, Vertx vertx, RQVerticle verticle) {
public void init(#Observes StartupEvent e, Vertx vertx, RQVerticle verticle) {
DeploymentOptions options = new DeploymentOptions()
.setInstances(2);
vertx.deployVerticle(verticle,options).await().indefinitely();
}
}
Std Out - first main class looks good
2021-09-15 15:48:12,052 INFO [org.acm.RQVerticle] (vert.x-eventloop-thread-2) Creating RabbitMQ Connection after Quarkus successful initialization
2021-09-15 15:48:12,053 INFO [org.acm.RQVerticle] (vert.x-eventloop-thread-3) Creating RabbitMQ Connection after Quarkus successful initialization
Std Out - second main class
2021-09-22 15:48:11,986 ERROR [io.qua.run.Application] (Quarkus Main
Thread) Failed to start application (with profile dev):
java.lang.IllegalArgumentException: Can't specify > 1 instances for
already created verticle

Vaadin with Apache CXF SOAP service

I am new to Vaadin, just generated the application in Vaadin web site and built it locally. Then I added Apache CXF SOAP service to it, but I am unable to use the Tomcat that Vaadin is using, but instead I load SOAP in Jetty using:
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>${cxf.version}</version>
<scope>compile</scope>
</dependency>
My Vaadin application is:
#SpringBootApplication
#Theme(value = "iciclient", variant = Lumo.DARK)
#PWA(name = "ICI Client", shortName = "ICI Client", offlineResources = {"images/logo.png"})
public class Application extends SpringBootServletInitializer implements AppShellConfigurator {
public static void main(String[] args) {
LaunchUtil.launchBrowserInDevelopmentMode(SpringApplication.run(Application.class, args));
try {
System.out.println("Starting IciEventClient");
Object implementor = new IciEventServiceSoap12Impl();
String address = "http://localhost:8081/ici/IciEventService";
Endpoint.publish(address, implementor);
// http://localhost:8081/ici/IciEventService?WSDL
} catch (Exception e) {
e.printStackTrace();
}
}
}
While this works, I would like to get rid of separate Jetty dependency and run the SOAP service in Vaadin Tomcat (localhost:8080).
Should be simple but I can't figure out how to do it.
I think that it needs a separate servlet and route, but I don't know how to add them.
There is no web.xml in the Vaadin application, for example.
I am not familiar with Apache CXF, but based on CXF docs and the sample project I think I got it to work.
I downloaded a new Vaadin 14/Java 8 project from start.vaadin.com, and did the following:
Added the dependency
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-spring-boot-starter-jaxws</artifactId>
<version>3.4.3</version>
</dependency>
Created a web service
import javax.jws.WebMethod;
import javax.jws.WebService;
#WebService
public class Test {
#WebMethod
public String test() {
return "This works";
}
}
Exposed it as a bean in my Application class
import javax.xml.ws.Endpoint;
import org.apache.cxf.Bus;
import org.apache.cxf.jaxws.EndpointImpl;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.vaadin.artur.helpers.LaunchUtil;
import org.vaadin.erik.endpoint.Test;
#SpringBootApplication
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
LaunchUtil.launchBrowserInDevelopmentMode(SpringApplication.run(Application.class, args));
}
#Bean
public Endpoint test(Bus bus) {
EndpointImpl endpoint = new EndpointImpl(bus, new Test());
endpoint.publish("/Test");
return endpoint;
}
}
That was it! At least I can now list the service definition at http://localhost:8080/services/Test?wsdl
The first documentation link lists some configurations you can do, for example to change the /services path. The example project shows how to configure Spring actuator metrics if that is something you need.
You might want to create a separate #Configuration-annotated class for all your service #Bean definitions.
If you don't want to use the starter dependency, this Baeldung article looks promising.

Adding REST route to an existing Jetty endpoint in Camel at runtime

I have been inventing a way how to work around the problem of adding consumers to a jetty endpoint (it does not allow multiple consumers). The way we do it in our company is to build our own router and a broadcasting endpoint which consumes from jetty and routes requests to underlying "subscriptions". Only one of them will eventually process the request. It kind of works but it's not completely ok, since recently when updating to latest Camel we have found our custom built component to leak memory and in general I consider using built-in functionality over custom hacks.
I started investigating the Camel REST API and found it very nice and pretty much replacing our home-grown component apart from one thing - you cannot re-configure it at runtime - you have to stop the context basically for this to work. Below I include my unit test with a happy path and the path that fails. Frankly I think is a bug, but if there is a legitimate way to achieve what I want, I'd like to hear sound advice:
package com.anydoby.camel;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.commons.io.IOUtils;
import org.junit.Before;
import org.junit.Test;
/**
* Test tries to add/remove routes at runtime.
*/
public class RoutesTest {
private DefaultCamelContext ctx;
#Before
public void pre() throws Exception {
ctx = new DefaultCamelContext();
new RouteBuilder(ctx) {
#Override
public void configure() throws Exception {
restConfiguration("jetty").host("localhost").port(8080);
rest("/")
.get("/issues/{isin}").route().id("issues")
.process(e -> e.getOut().setBody("Here's your issue " + e.getIn().getHeader("isin"))).endRest()
.get("/listings").route().id("listings").process(e -> e.getOut().setBody("some listings"));
}
}.addRoutesToCamelContext(ctx);
ctx.start();
}
#Test
public void test() throws IOException {
{
InputStream stream = new URL("http://localhost:8080/issues/35").openStream();
assertEquals("Here's your issue 35", IOUtils.toString(stream));
}
{
InputStream stream = new URL("http://localhost:8080/listings").openStream();
assertEquals("some listings", IOUtils.toString(stream));
}
}
#Test
public void disableRoute() throws Exception {
ctx.stopRoute("issues");
ctx.removeRoute("issues");
try (InputStream stream = new URL("http://localhost:8080/issues/35").openStream()) {
fail();
} catch (Exception e) {
}
new RouteBuilder(ctx) {
#Override
public void configure() throws Exception {
rest().get("/issues/{isin}/{sedol}").route().id("issues")
.process(e -> e.getOut()
.setBody("Here's your issue " + e.getIn().getHeader("isin") + ":" + e.getIn().getHeader("sedol")))
.endRest();
}
}.addRoutesToCamelContext(ctx);
{
InputStream stream = new URL("http://localhost:8080/issues/35/65").openStream();
assertEquals("Here's your issue 35:65", IOUtils.toString(stream));
}
}
}
The disableRoute() test fails since I cannot add another consumer to an existing endpoint.
So my question is - "is there a way to add a new URL mapping to a restful camel-jetty endpoint"? If you do it during first configuration it works fine, but when later you want to reconfigure one of the routes the error is:
org.apache.camel.FailedToStartRouteException: Failed to start route because of Multiple consumers for the same endpoint is not allowed: jetty:http://localhost:8080/issues/%7Bisin%7D/%7Bsedol%7D?httpMethodRestrict=GET

Changing parameters in a JAX-WS web service

I'm creating some web services using JAX-WS and the java SE build-in server. Every time I add a new parameter on a web service i need to change the URL it's published to. Otherwise the new parameters always get a null value. How can I make this work without changing the URL?
Here's the main class code with the publishing code:
import javax.xml.ws.Endpoint;
import pickate.AmazonMail;
import pickate.FacebookStream;
class Main {
public static void main(String[] args) {
Endpoint.publish("http://localhost:8888/pickate/amazonmail", new AmazonMail());
Endpoint.publish("http://localhost:8888/pickate/facebookstream", new FacebookStream());
}
}
And the implementation of one of the webservices
package pickate;
import java.util.List;
import javax.jws.Oneway;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
// Other imports go here
#WebService
public class FacebookStream
{
public FacebookStream()
{
}
#WebMethod
#Oneway
public void sendNotification(
#WebParam(name = "receivers") List<String> receivers,
#WebParam(name = "fbtoken") String fbtoken,
#WebParam(name = "body") String body,
)
{
// Some interesting stuff goes here
}
}
It was indeed the client caching up the WSDL file. It seems the PHP Soap Extension (which is what i'm using on the client-side) does it by default.

Categories

Resources