Wildfly-Swarm : Class not in Classpath in CDI wildfly-swarm microservice - java

I am building multiple microservices with wildfly-swarm. I have one microservice that works correctly and one that fails while trying to start the CDI-Container because the class com.google.common.cache.Cache is not present in the Classpath but is referenced from an ApplicationScoped Bean. This is the stacktrace i get:
WELD-001474: Class [...].core.framework.timeseries.cache.TimeseriesDataCache is on the classpath, but was ignored because a class it references was not found: com.google.common.cache.Cache from [Module \"deployment.43ef34ae-45a8-4468-aa7d-40d75c0f0a79.war:main\" from Service Module Loader].
The strange thing is that both microservices use this class from the same maven dependency, but the other service works like intended. The main difference is, that the failing microservice uses Ribbon (via the swarm-dependency) and this brings a second Guava-Dependency (version 14.0.1). Hence i tried to exclude the guava-dependency from Ribbon (coming from the netflix-guava module) in any way (by excluding the guava dependency directly on the main Ribbon artifact and by excluding the netflix-guava dependency from the main Ribbon aratifact and then adding the dependency for netflix-guava myself and excluding it there).
I was partly successful by manually excluding the guava artifact for version 14.0.1 in my main-class but that caused other issues and in my opinion would not be a viable option (since this behaviour could lead to us having to manually exclude any artifact that is shipped with some dependency if it causes problem.
So my question would be: is there any way to work around this behaviour? Or a solution i did not get from the swarm documentation? I am really lost right now and any help/idea is greatly appreciated. Just in case here is the code that builds my deployment as i would like it to work (without manually adding the neccessary artifacts to exclude unwanted ones):
public static void main(String[] args) throws Exception {
Swarm container = new Swarm(args);
final String offset = args.length > 0 ? args[0] : null;
container = container.socketBindingGroup(new SocketBindingGroup("default-sockets","public","${jboss.socket.binding.port-offset:" +
offset != null ? offset : "0" +
"}"));
container
.fraction(new JAXRSFraction())
.fraction(new CDIFraction())
.fraction(new LoggingFraction());
JAXRSArchive jaxrsArchive = ShrinkWrap.create(JAXRSArchive.class);
jaxrsArchive.addAsResource(new ClassLoaderAsset("META-INF/beans.xml", FormulaDeployment.class.getClassLoader()), "classes/META-INF/beans.xml");
jaxrsArchive.addAllDependencies();
jaxrsArchive.addAsLibrary(container.createDefaultDeployment());
jaxrsArchive.as(RibbonArchive.class).advertise();
container.start().deploy(jaxrsArchive);
}

Related

Is there a way to get list of loaded properties file in SpringBoot application?

I'm facing an issue on only one platform when I'm trying to execute mvn clean install. As part of the build we compile multiple component and last we execute functional testing using wiremock. It is supposed to pick specific configuration from function testing profile and default properties should be picked from application.properties file. But for some reason same code isn't able to find the properties mentioned in these file. So, just wondering if somehow, if I can get the list of properties files being loaded during wiremock ? This will give some clue on why isn't expected properties files are being picked ?
All properties files are located inside :
src/main/resources
And, following from test class.
#ContextConfiguration(classes = SampleFTConfiguration.class)
public class SampleControllerTest{
//test method
}
#ComponentScan("com.xxx.xxx.xxx.ft")
#PropertySource("classpath:application-test.properties")
public class SampleFTConfiguration{
}
Note : I'm not expecting anyone to fix the issue, all I wanted to know, if we can get the name of loaded property files ?
After searching and trying out for a while, looks like ConfigurableEnvironment is what you're trying to find.
The code is pretty simple. However I think it's better to debug and check the configurableEnvironment value directly, so you can adjust the code to your needs (remove filter name, etc).
#Autowired
private ConfigurableEnvironment configurableEnvironment;
#Test
public void getProperties() {
Map<String, Object> mapOfProperties = configurableEnvironment.getPropertySources()
.stream()
.filter(propertySource -> propertySource.getName()
.contains("application-test.properties"))
.collect(Collectors.toMap(PropertySource::getName, PropertySource::getSource));
mapOfProperties.values()
.forEach(System.out::println);
}
the code will printed out
{properties-one=value-for-properties-one,
properties-two=value-for-properties-two}
with my application-test.properties value
properties-one=value-for-properties-one
properties-two=value-for-properties-two
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/env/ConfigurableEnvironment.html
Ok, following the test definition please make sure that:
You should run the test with spring runner (spring extension if you're on JUnit5). So you should place the annotation #RunWith(SpringRunner.class) (or #ExtendsWith(SpringExtension.class) for junit 5)
The property source you're using is application-test.properties. You've said that the properties file is located in src/main/resources but the file name probably implies that it should reside in src/test/resources
To troubleshoot context configuration on app startup (for those who don't have an access to app sources) you can add
logging:
level:
org.springframework.boot.context.config: trace
to your application.yml to get filenames:
2022-10-26 13:29:45.977 TRACE [,,] 16522 --- [main] o.s.b.context.config.ConfigDataLoaders : Loading file [config/application-app.
yml] using loader org.springframework.boot.context.config.StandardConfigDataLoader
2022-10-26 13:29:45.977 TRACE [,,] 16522 --- [main] o.s.b.context.config.ConfigDataLoaders : Loading file [config/application-ppe.
yml] using loader org.springframework.boot.context.config.StandardConfigDataLoader
Adding org.springframework.boot.context.properties: trace doesn't help much though. Some user-defined properties get logged, however others don't.

Splitting resources.groovy (Grails 2.5) to make it modular

resources.groovy of my Grails project is growing, so I am thinking of splitting it in some logical files that will be easier to mantain. After reading some questions, blogs, i got to know about importBeans or loadBeans. This works well only when application is run using grails run-app. However we I create a war and deploy it on JBoss, the custom bean files are not accessible.
Also tried below with no luck - mentioned here -
Load spring beans from custom groovy files in grails app
grails.war.resources = { stagingDir, args ->
copy(todir: "${stagingDir}/WEB-INF/classes/spring") {
fileset(dir:"grails-app/conf/spring") {
include(name: "datasourceDefinitions.groovy")
exclude(name: "resources.groovy")
}
}
}
I have added datasourceDefinitions.groovy in grails-app/conf/spring.
Please help.
The problem is due to the Spring bean configuration files are moved into the folder "WEB-INF/classes/spring/"(make sure the file is packaged in the.war) inside the WAR file. As what I did was locate the resource path in resources.groovy.
def loadFromFile = { name ->
importBeans("file:grails-app/conf/spring/"+name)
}
def loadFromWar = { name ->
def resource = application.parentContext.getResource("WEB-INF/classes/spring/"+name)
loadBeans(resource)
}
def loadResource = application.isWarDeployed() ? loadFromWar : loadFromFile
loadResource "datasourceDefinitions.groovy"
loadResource "anotherBean.groovy"

Max number of request parameters reached in spring boot application

I have a spring boot application that's using multi-upload to update sometimes large amounts of files 10K+. In those cases, I'm hitting this exception. I'm guessing it's looking at my "files" parameter and seeing that it's an array > 10K and flagging this exception. I'm also sending another parameter that's an array of strings that are associated with the list of files, its size being the number of files, > 10K
java.lang.IllegalStateException: More than the maximum number of request parameters (GET plus POST) for a single request ([10,000]) were detected. Any parameters beyond this limit have been ignored. To change this limit, set the maxParameterCount attribute on the Connector.
at org.apache.tomcat.util.http.Parameters.addParameter(Parameters.java:204) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
at org.apache.catalina.connector.Request.parseParts(Request.java:2860) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
at org.apache.catalina.connector.Request.parseParameters(Request.java:3177) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
at org.apache.catalina.connector.Request.getParameter(Request.java:1110) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
at org.apache.catalina.connector.RequestFacade.getParameter(RequestFacade.java:381) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
I understand the exception, but I'm trying to figure out where in my application.properties I can adjust this. I've set spring.http.multipart.max-file-size and spring.http.multipart.max-request-size there. I'm not finding anything equivilant to the maxParameterCount in this source.
Also, assuming there's a way I can set it for the instance running locally with spring boot (tomcat embedded), will the change also work in the deploymenet environment, or does that require changing a tomcat configuration?
Update: I found a solution that works when running locally with spring boot. I assume since this is changing the Tomcat Embedded instance, that this wouldn't apply to a deployed full tomcat environment -- I am wondering if there's a solution that would work in both tomcat instances.
#Configuration
public class TomcatCustomizationConfiguration {
#Bean
public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
final int maxHttpRequests = 50000;
TomcatEmbeddedServletContainerFactory tomcatFactory = new TomcatEmbeddedServletContainerFactory();
tomcatFactory.addConnectorCustomizers(connector -> connector.setMaxParameterCount(maxHttpRequests));
return tomcatFactory;
}
}
According to the Spring documentation, you can add missing configuration yourself by extending the WebServerFactoryCustomizer:
If a configuration key doesn’t exist for your use case, you should then look at WebServerFactoryCustomizer. You can declare such a component and get access to the server factory and the chosen web stack.
As there is no server.tomcat.max-parameter-count configuration yet, you can add it kind of the same way as OPs configuration code:
#Configuration
public class TomcatCustomizationConfiguration implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
#Value("${server.tomcat.max-parameter-count:10000}")
private int maxParameterCount;
#Override
public void customize(TomcatServletWebServerFactory factory) {
factory.addConnectorCustomizers(connector -> connector.setMaxParameterCount(maxParameterCount));
}
}
N.B. I actually found the solution at this blog.

JCA Connector Classloading

in my Scenario, i try to use JCA Adapters to connect to an external storage - just to try this feature of J2EE.
I use JBoss EAP 7 and its packed implementation ironjacamar.
i deploy an adapter.rar, which contains an adapter.jar (this contains the Connection and ConnectionFactory Interfaces and all implementations) and META-INF/ironjacamar.xml.
I then deploy a app.war file, containing a Bean with an annotated field:
#RequestScoped
public class Bean {
...
#Resource(lookup = "java:/eis/StorageConnectionFactory")
private StorageConnectionFactory connectionFactory;
}
The war also contains the adapter.jar as library - as it needs to know of all the classes at runtime (NoClassDefFound etc.)
To my amazement, the Connector itself seems to work - as is get the Exception:
java.lang.IllegalArgumentException: Can not set conn.StorageConnectionFactoryImpl field Bean.connectionFactory to conn.HsmConnectionFactoryImp
and on ommitting the interfaces even:
#Resource(lookup = "java:/eis/StorageConnectionFactory")
private StorageConnectionFactoryImpl connectionFactory;
still
java.lang.IllegalArgumentException: Can not set conn.StorageConnectionFactoryImpl field Bean.connectionFactory to conn.HsmConnectionFactoryImp
I see that the Problem is, that the adapter.rar does nto share the same classloader as the app.war and both contain the corresponding classes, leading to a sort of ClassCastException - how do i solve this issue correctly?
It seems you haven't configure resource adapter properly.
See the below guide, it will help you to configure:
https://access.redhat.com/documentation/en/red-hat-jboss-enterprise-application-platform/version-7.0/configuration-guide/#configuring_jca_subsystem

Spring : How to inject manually a bean which is not managed by actual application?

Here the context :
we have a java library, which is a factory code.
this library is deployed directly on Tomcat
Application "A", "B" & "C" use this library (jar) to compile, and it is the deployed version on Tomcat which is used when an application call it.
In the library, we have these packages :
- old.service
- old.service.impl
- new.service
- new.service.impl
The old services are some classic classes with setters. It is a spring bean declared in XML configuration of application "A".
In the new services, we have an annotated class (#Service) with some #Autowired attributes in order to be managed by application "B" & "C" which have an autoscan in XML configuration.
We would like to change the implementation of a old services, in order to use the new one without changing anything in application "A".
For that, we can call the new class from the older. But the pb is Spring......
How can we instanciate the new class, and the #autowired attributes ?
Can we instanciate manually the new class in older class, and instanciate attributes by reflection ?
Thank you.
ps: there is no XML configuration in the java library.
Two ways:
In app A where you instantiate old.service.impl Spring bean inject reference to new.service.impl into it
< bean class="old.service.impl">
< property name="newService" ref="newServiceBean"/>
< /bean>
< bean class="new.service.impl" id="newServiceBean" />
Obviously this means you will have to modify your old.service.impl to add setter for "newService", then use it in old service impl code
Get "new.service.impl" reference directly from web application context:
WebApplicationContextUtils.getWebApplicationContext(servletContext).getBean(newServiceImpl.class);
You will need to obtain ServletContext in this case. One way to do it is to get it from HttpRequest
Option 1 is preferred - you are not relying on running inside a servlet then, and requires very few changes in your code.

Categories