Create spring boot restful Webservices without #EnableAutoconfig - java

I have a Spring boot project which is packaged into jar and it functionalities are related to MQ and database. I want to now host a webservice on that project however I cannot use #EnableAutoConfiguration or #SpringBootApplication. The webservice to be created is a simple GET service that will return a integer value. I have tried following steps but still the webservice doesnt start.
Added Spring-webmvc, spring-web, spring-boot-web ,spring-tomcat jars to pom.xml
Changed the artifact type from jar to war in pom.xml
Added #EnableWebMvc in the config class
Added a class with #Controller and #RequestMapping
Created a bean for EmbeddedServletContext for Tomcat.
What have I missed?
Some blogs mentioned about creating a WebApplicationContextInitializer and some mentioned about creating a Dispatcher servlet.
My question is what all beans do I need to manually create to bring up my webservice (as I cannot use EnableAutoConfig)

Related

Spring-Boot Components from other jar are not in context

I have following situation. JDK 17, Spring-Boot: 2.6.2. A gradle multi-project. One project is a library (java-library, no spring boot plugin). Another project is a spring boot application with spring boot plugin. Generally, the spring dependency management plugin is not used, gradle platform concept is used instead. Application project includes dependency to library project per "implementation(project(':.."
Library:
Library project and spring boot application project have different packages.
like:
library root package is a.b.c and application root package is a.b.d
Library project has in root package of the package hierarchy a configuration class (for example a.b.c.LibraryConfig) which is annotated with #Configuration annotation and with #ComponentScan annotation, which has attribute "basePackageClass" pointing to this configuration class:
#Configuration
#ComponentScan(basePackageClass = LibraryConfig.class)
public class LibraryConfig {
Inside of library hierarchy (so in packages a.b.c.*) are services and rest controllers.
Application:
Application has in root package of the package hierarchy (for example in a.b.d) application class and in sub-package "config" it has configuration class (would be for example a.b.d.config.AppConfig), which imports the configuration class from library:
#Configuration
#Import(a.b.c.LibraryConfig.class)
public class AppConfig {
Problem:
Classes from the library are there in runtime - we are able to load them.
No any component of the library is in context (not registered), neither services nor rest controllers.
What are we missing?
We have tried different constellations of imports and scans. Also added component scan to application class and set there particular packages. Nothing helped.
The problem was, that the main application was using spring boot indexing annotation processor and library - not. So after enabling the annotation processor for library problem was solved.

Spring Boot with devtools call SOAP API

I am deploy an project Spring Boot, using devtools(spring-boot-devtools) and call a Soap service.
I generate the Soap class into /src/main/resources/templates/generated
and add this folder as Source Code.
Because when call this Soap service, its have a problem:
java.lang.IllegalArgumentException: ...ClassV11PortType referenced from a method is not visible from class loader
So, I was add the spring-devtools.properties file to /src/main/resources/META-INF/spring-devtools.properties
and add this line to spring-devtools.properties file:
restart.exclude.mygeneratedclasses=/[packageOfGeneratedClass].class
Then now, I can call the SOAP service successful.
But now, my project cannot reload automatically when i modified some code.
I was try to edit some code anywhere and save but not luck, my project doesnot reload.
Instead of excluding generated files, you can try to include JAR responsible for loading these classes into restart classloader (used in spring-devtools).
For dependency com.sun.xml.ws:jaxws-rt:2.3.2-1, update /src/main/resources/META-INF/spring-devtools.properties like this:
restart.include.jax=/jaxws-rt.*\.jar
Github issue reference: Devtools cannot be use with jaxws-ri #19379

Spring Cloud Config in web application

I have legacy Java web application (with Spring MVC and web.xml) which are deployed in Tomcat. I want to swtich configuration to Spring Cloud Config.
Structure of this application (just simplify it for example) - jar file with Spring Controller and services, which I want to do Spring Cloud Config compatible. And another module with web.xml, which add jar as dependency.
I added bootstrap.yml with application name to module, which packaged to jar in "resources" folder and add "EnableAutoConfiguration" and "SpringBootApplication" annotations, but got exception:
java.lang.IllegalArgumentException: Could not resolve placeholder 'foo' in string value "${foo}"
at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:174)
at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:126)
at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:219)
at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:193)
Config server is run. I wrote simple client, which I run via main method and it works, but when I deploy legacy app in Tomcat - it doesn't work.
Can someone help with it?

Using Spring boot/cloud with Amazon AWS lambda does not inject values

I have an AWS lambda RequestHandler class which is invoked directly by AWS. Eventually I need to get it working with Spring Boot because I need it to be able to retrieve data from Spring Cloud configuration server.
The problem is that the code works if I run it locally from my own dev environment but fails to inject config values when deployed on AWS.
#Configuration
#EnableAutoConfiguration
#ComponentScan("my.package")
public class MyClass implements com.amazonaws.services.lambda.runtime.RequestHandler<I, O> {
public O handleRequest(I input, Context context) {
ApplicationContext applicationContext = new SpringApplicationBuilder()
.main(getClass())
.showBanner(false)
.web(false)
.sources(getClass())
.addCommandLineProperties(false)
.build()
.run();
log.info(applicationContext.getBean(SomeConfigClass.class).foo);
// prints cloud-injected value when running from local dev env
//
// prints "${path.to.value}" literal when running from AWS
// even though Spring Boot starts successfully without errors
}
}
#Configuration
public class SomeConfigClass {
#Value("${path.to.value}")
public String foo;
}
src/main/resources/bootstrap.yml:
spring:
application:
name: my_service
cloud:
config:
uri: http://my.server
failFast: true
profile: localdev
What have I tried:
using regular Spring MVC, but this doesn't have integration with #Value injection/Spring cloud.
using #PropertySource - but found out it doesn't support .yml files
verified to ensure the config server is serving requests to any IP address (there's no IP address filtering)
running curl to ensure the value is brought back
verified to ensure that .jar actually contains bootstrap.yml at jar root
verified to ensure that .jar actually contains Spring Boot classes. FWIW I'm using Maven shade plugin which packages the project into a fat .jar with all dependencies.
Note: AWS Lambda does not support environment variables and therefore I can not set anything like spring.application.name (neither as environment variable nor as -D parameter). Nor I can control the underlying classes which actually launch MyClass - this is completely transparent to the end user. I just package the jar and provide the entry point (class name), rest is taken care of.
Is there anything I could have missed? Any way I could debug this better?
After a bit of debugging I have determined that the issue is with using the Maven Shade plugin. Spring Boot looks in its autoconfigure jar for a META-INF/spring.factories jar see here for some information on this. In order to package a Spring Boot jar correctly you need to use the Spring Boot Maven Plugin and set it up to run during the maven repackage phase. The reason it works in your local IDE is because you are not running the Shade packaged jar. They do some special magic in their plugin to get things in the right spot that the Shade plugin is unaware of.
I was able to create some sample code that initially was not injecting values but works now that I used the correct plugin. See this GitHub repo to check out what I have done.
I did not connect it with Spring Cloud but now that the rest of the Spring Boot injection is working I think it should be straightforward.
As I mentioned in the comments you may want to consider just a simple REST call to get the cloud configuration and inject it yourself to save on the overhead of loading a Spring application with every request.
UPDATE: For Spring Boot 1.4.x you must provide this configuration in the Spring Boot plugin:
<configuration>
<layout>MODULE</layout>
</configuration>
If you do not then by default the new behavior of the plugin is to put all of your jars under BOOT-INF as the intent is for the jar to be executable and have the bootstrap process load it. I found this out while addressing adding a warning for the situation that was encountered here. See https://github.com/spring-projects/spring-boot/issues/5465 for reference.

Can I auto-discover jaxws:endpoints for beans.xml based on annotated #WebService classes / interfaces?

I created a cxf/spring project with:
mvn archetype:create -DarchetypeGroupId=org.apache.cxf.archetype -DarchetypeArtifactId=cxf-jaxws-javafirst
The resulting project has a HelloWorld.java interface annotated as a #WebService, and a HelloWorldImpl.java annotated with an endpointInterface=mypackage.HelloWorld.
There's a beans.xml file containing:
<jaxws:endpoint
id="helloWorld"
implementor="mypackage.HelloWorldImpl"
address="/HelloWorld" />
If I want to add more services, it looks like I'm expected to keep adding new endpoints in beans.xml. Since the classes are already annotated as #WebService, why can't it auto-discover any new services within some package / search path? Is there some way I can configure this project to do that?
Basically, I'm just trying to avoid repeating myself. The information is already going to be in the annotations so I don't want to have to edit additional files each time I add a service.
If I remove the jaxws:endpoint tag in beans.xml, and then mvn install tomcat:run, localhost:8080/myArtifactId just shows me a page saying there are not services defined.
When you use the CXF you should know that all the endpoints you added are managed by the CXFServlet that uses Spring context to find them out. beans.xml is a Spring context's config file. So if you remove the <jaxws:endpoint/> node from that file your Spring context and hence the CXFServlet will have no way to find your service endpoints.

Categories