#ComponentScan doesn't work in my Minecraft plugin - java

I am creating a plugin for Minecraft server in which I use this SpringFramework. And I use JavaConfig + Annotations, but my project couldn't normally in runtime due to #ComponentScan.I discovered the fact that this is only in the Minecraft server plugin, but I could be wrong. Although in a normal Spring project everything is scanned normally
I have a directory like this: me.stordshally.testplugin
Main class (me.stordshally.testplugin.Main)
public class Main extends JavaPlugin {
private User user;
#Override
public void onEnable() {
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
user = context.getBean(FacebookUser.class);
}
}
JavaConfig (me.stordshally.testplugin.SpringConfig)
#Configuration
#ComponentScan
public class SpringConfig {
}
Component (me.stordshally.testplugin.FacebookUser) (exists also User interafce in that directory)
#Component
public class FacebookUser implements User {
#Override
public String getNet() {
return "Facebook";
}
}
And at the moment context.getBean I get this exception:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'me.stordshally.testplugin.FacebookUser' available
onEnable - starts when the server starts, consider it as an analog of psvm.
Why is this happening? How can I fix my ComponentScan?
Github for test: https://github.com/Prot-CN/SpringBug

Ideally, some more information would have helped, however, I do not know what the src package structure looks like but try explicitly providing base package like
#ComponentScan(basePackages = "me.stordshally.testplugin")
This tells spring explicitly where to look for potentials beans to create. Please make sure you provide the path under which all your beans are located.
If this doesn't work, it means for some reason your application is failing, try running your app in debug mode by setting log-level to debug. It will tell where the issue is. it's the best I can tell from the info provided, Hope this helps.

Related

Spring autowires beans in different order when running from jar vs IntelliJ - Failed to start bean 'subProtocolWebSocketHandler' No handlers

I am building a spring web socket app and I am facing the following issue.
When I am running the app using IntelliJ everything is fine and the app starts up just fine.
When I am building the fat jar with spring boot maven plugin and starting up the app using java -jar the app is failing to start with the following error
Failed to start bean 'subProtocolWebSocketHandler'; nested exception is java.lang.IllegalArgumentException: No handlers
at org.springframework.web.socket.messaging.SubProtocolWebSocketHandler:start()
My spring web socket config looks like this
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
private WebSocketMessageBrokerStats webSocketMessageBrokerStats;
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic")
.setHeartbeatValue(new long []{webSocketsProperties.getClientHeartbeatsSecs() * 1000, webSocketsProperties.getServerHeartbeatsSecs() * 1000})
.setTaskScheduler(heartBeatScheduler());
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/gs-guide-websocket").setAllowedOrigins("*").withSockJS();
}
#Autowired
public void setWebSocketMessageBrokerStats(WebSocketMessageBrokerStats webSocketMessageBrokerStats) {
this.webSocketMessageBrokerStats = webSocketMessageBrokerStats;
}
}
The reason why the above error is happening is because when I run the app using the jar the method
#Autowired(required = false)
public void setConfigurers(List<WebSocketMessageBrokerConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addAll(configurers);
}
}
inside DelegatingWebSocketMessageBrokerConfiguration which is supposed to autowire my WebSocketConfig is invoked after the
#Override
protected void registerStompEndpoints(StompEndpointRegistry registry) {
for (WebSocketMessageBrokerConfigurer configurer : this.configurers) {
configurer.registerStompEndpoints(registry);
}
}
in DelegatingWebSocketMessageBrokerConfiguration which is causing the no handlers error. When I am starting the app through IntelliJ this is happening in reverse and everything is fine.
Does anyone have any idea why this is happening and what might be the reason causing it?
Is there any chance that loading classpath is happening in a different order in a jar vs in IntelliJ and that confuses spring?
EDIT
My WebSocketConfig class is slightly different than what I have put above. I am autowiring WebSocketMessageBrokerStats in it with setter injection. I have updated the code above. The reason why I didn't put this in my initial question is that I thought it was insignificant. But it is not. Answer is coming below...
Thanks a lot in advance
(let me know if you want more technical details from my side)
Nick
So after playing around with my code I figured out that the issues is the injection of the WebSocketMessageBrokerStats bean. Apparently this is causing WebSocketConfig bean (which is a special type of config since it implements WebSocketMessageBrokerConfigurer) to be ready at a later stage in the Spring Context Initialisation leaving List<WebSocketMessageBrokerConfigurer> configurers empty when it is checked by the registerStompEndpoints().
So the solution was to create a second configuration class and move the WebSocketMessageBrokerStats bean and all the operations on it in the new config file.
The above is fixing the jar file and I am able to run it with java -jar, however I have no idea how IntelliJ was able to run the app successfully without the fix.

How to read properties file with IntelliJ

I have a Spring Boot service, and I'm using IntelliJ to run it. I have a file call "application.properties" in my resources folder. I want intelliJ to read that .properties file, how do I do that? the only way I get it to use the properties in .properties file is to add them directly to Environment VM Option.
I tried doing things like
-Dspring.config.location:/src/main/resources/application.properties but that doesnt work.
Folder Structure:
Services
-src
-main
-resources
-application.properties
-target
-pom.xml
#Component
#Configuration
#EnableAutoConfiguration
#PropertySource({ "classpath:application.properties"})
#EntityScan(basePackages = "com.org")
public class AppConfig {
}
In case of Spring Boot, you don't have to pass any option when starting boot application.
When Spring boot application loads, it automatically checks if properties file exists in certain locations including src/main/resources, src/main/resources/config.
If you keep your properties file in these folders, the app automatically picks up the files and register properties. So in your AppConfig you don't need #Component, #EnableAutoConfiguration, #PropertySource, and #EntityScan because #Configuration already includes #Component which enables #Value to work.
I think the problem may arise when you call the property in the constructor of AppConfig because when the class is being constructed the #Value is not injected yet.
If you want to check if the property value is injected by Spring you can make a small test in the application class such as following
#SpringBootApplication
public class ApppropApplication {
#Autowired
private AppConfig appConfig;
public static void main(String[] args) {
SpringApplication.run(ApppropApplication.class, args);
}
#PostConstruct
public void init(){
System.out.println(appConfig.getTestProperty());
}
}
If your problem still exists, it would be great to provide more info (error logs and entire class structure)
Hope this helps! Happy Coding :)
The ideal way for your springboot project to include the properties file would be to import using the annotation "#PropertySource" in your starter class. Please re check your starter class. It should include something like this below
#PropertySource({ "classpath:application-example.properties"})
#EntityScan(basePackages = "com.org")
public class YourProjectLauncher extends SpringBootServletInitializer {
#Value("${your.db.url}")
private transient String dataSourceUrl;
#Value("${your.db.username}")
private transient String userName;
#Value("${your.db.password}")
private transient String password;
#Value("${your.db.driver-class-name}")
private transient String driverClass;
public static void main(String... args) {
SpringApplication.run(YourProjectLauncher.class, args);
}
Let me know if you have already done this and still facing the issue.
Also it would be best if you add the starter class in your question, that way it is easier to analyse the problem you are facing.
Note - If you have already done this, please add more information to the question, will be happy to help.

Spring Boot auto configuration order from external dependency

I have a problem trying to get my autoconfiguration working. I have two jars as follows, each have a spring.factories file where these two are enabled for EnableAutoConfigurationProperties.
This configuration is in my-package-mock.jar, it depends on my-package-real.jar below:
package org.packages.package.packageA;
#Configuration
#AutoConfigureBefore(AutoConfigurationB.class)
public class AutoConfigurationA {
#Bean
public MyService mockService() {
return new MyMockService();
}
}
This configuration is in my-package-real.jar:
package org.packages.package.packageB;
#Configuration
#ConditionalOnMissingBean(MyService.class)
public class AutoConfigurationB {
#Bean
public MyService realService() {
return new MyRealService();
}
}
Now the idea is that if my-package-mock.jar is included then AutoConfigurationB will not be processed as A is ordered to be before and by the time it gets to B MyService is already defined.
However, it does not work when used in a third project that includes these jars. It looks like the AutoConfigureOrder annotation is skipped when loading these jars from the classpath and these configurations are processed in the order the jvm loads these classes. In my particular case it does B first and at that point MyService is not yet defined and thus will instantiate the RealService bean. How can I get this to work?
Obviously this is a small example where a #Primary annotation on the mock will do the job, but that is not what I'm looking for.
Edit: it seems if the #SpringBootApplication annotated main is not a part of the package where these configurations are then things do work. E.g. the annotation is not in "org.packages.package" but "org.somewhereelse" then things work.
package org.packages.package;
#SpringBootApplication
public class TestApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(Collections.singletonList(TestApplication.class).toArray(), args);
}
}
#AutoConfigureBefore and #AutoConfigureAfter only apply when a configuration class is loaded as a result of auto-configuration being enabled and it being listed in spring.factories. When your auto-configuration classes are in org.packages.package (or a sub-package) and your main application class is in the same package, they're being found by Spring Framework's standard component scanning. This happens because #SpringBootApplication enables component scanning for the package of the class that it's annotating. As a result of this the auto-configuration-specific ordering doesn't apply.
To avoid the problem, you should places your auto-configuration classes in a package that isn't used by any application code.

No matching bean of type found for dependency: Spring MVC

I am getting this error when trying to use #autowire, #configuration, #bean, #Repository in my Spring MVC project
Could not autowire field: private com.sachin.dao.StockDaoImpl com.sachin.myapp.HomeController.stockDao;
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.sachin.dao.StockDaoImpl] found for dependency:
Please let me know what mistake I am making. I am new to Spring MVC and dependency injection.
Here is my controller code. I am trying to inject StockDaoImpl in the controller.
#Controller
public class HomeController {
#Autowired
private StockDaoImpl stockDao;
#RequestMapping(value = "/stockgoogle/", method = RequestMethod.GET)
public #ResponseBody Stock stockGoogle(Locale locale, Model model) {
//StockDaoImpl stockDao = new StockDaoImpl();
Stock s=stockDao.listGoogle();
model.addAttribute("s", s );
return s;
}
}
My Service Implementation is below. I have used #Repository annotation here with "stockDao" which is my variable name in controller that I want to inject
#Repository("stockDao")
public class StockDaoImpl implements StockDao {
#Override
public Stock listGoogle() {
Stock s = null;
try {
... //some code
String name = rs.getString("Name");
s = new Stock(name);
...
} catch (Exception e) {
}
return s;
}
}
Also I have created a configuration class separately. I am using this to define my bean. I am only using this to specify bean and have not imported it anywhere in the code.
#Configuration
public class BeanConfiguration {
#Bean
public StockDaoImpl stockDao(){
return new StockDaoImpl();
}
}
Am I missing something here. From looking at the error it looks like the #Bean annotation is not visible to the factory. Do I have to do anything else other than annotating the #configuration class.
I might also be using the annotations in a wrong way. I could be making a mistake in how I am using #Autowired or #Repository.
Can you please help.
I think this might be your issue:
"Also I have created a configuration class separately. I am using this to define my bean. I am only using this to specify bean and have not imported it anywhere in the code."
Somewhere you need to tell Spring to look for BeanConfiguration. You can do this in your applicationContext.xml file (assuming you have one) as follows:
<context:component-scan base-package="com.sachin.config" />
This assumes BeanConfiguration is in the com.sachin.config package.
If you can't find where to put this it may be helpful to share your web.xml file.
I am having the described behaviour in a test class.
I work in IntelliJ 2020.1, with an old project build on:
Java 1.6
Spring 3.0.5.RELEASE
Maven POM 4.0.0
Maven 3.2.5
The test class starts as:
#Test
#ContextConfiguration(locations={ "classpath*:beans_sets/UhradyForIns.xml"})
#TransactionConfiguration(transactionManager="transactionManager", defaultRollback=false)
#DirtiesContext(classMode= DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class OdeslatJakobyAutomatickyUhraduTest extends TestBaseWithProperties {
private static final Logger log = LoggerFactory.getLogger(OdeslatJakobyAutomatickyUhraduTest.class);
#Autowired
U01UhradaBaseJpaDaoForTesting dao;
Sometimes suddenly, after some changes in POMs, Spring cannot autowire the dao giving exactly your messages. The dao is well described by its class and by XML context description but is suddenly invisible for Spring. I have tried many ways described on SO, adding annotations, changing configuration, but I have found, that it is all excessive. All I need is only to refresh the maven repositories 4 times by 4 different orders subsequently:
1. mvn -e -U clean install //In cmd line
2. Ctrl+Shift+A Reimport maven //in IntelliJ
3. double round arrows for refresh //in the maven window of IntelliJ
4. AGAIN REPEAT THE FIRST REFRESH.
After that, it is all OK and works fine till the next greater change in POMs.
Or... Till due to some inner reasons, Maven or IntelliJ damages the local jar repository without your participation. At least, I had a case, when I haven't touched the repository, but again that Could not autowire... message appeared. But this time only one
mvn -e -U clean install
was enough.
Obviously, there is/are some error(s) in the maven and/or maven plugin in IntelliJ. That problem is shown by Could not autowire... message. The repair is possible by a simple maven repository refresh or, in worse cases, demand a sequence of several different repository refreshes.
I understand that it has no obvious logic in that solution, and our developer's work resembles magic more and more. And this time, definitely, it is not your or my vice. Simply we have to adapt to existing errors. At least we know how to do it.

How to set Spring Active Profiles Pragmatically from a configuration class?

On a project I'm working on we have some old dependencies that define their own spring beans but need to be initialized from the main application. These beans are all constructed using spring profiles, i.e. "default" for production code and "test" for test code. We want to move away from using spring profiles, instead simply using #import to explicitly wire up our context.
The idea is to encapsulate all these old dependencies so that no other components need to care about spring profiles. Thus, from a test`s point of view, the application context setup can be described as follows:
#ContextConfiguration(classes = {TestContext.class})
#RunWith(SpringJUnit4ClassRunner.class)
public class MyTest {
//tests
}
TestContext further directs to two classes, one of which encapsulates the old dependencies:
#Configuration
#Import(value = {OldComponents.class, NewComponents.class})
public class TestContext {
//common spring context
}
To encapsulate the old components` need for profiles, the OldComponents.class looks as follows:
#Configuration
#Import(value = {OldContext1.class, OldContext2.class})
public class OldComponents {
static {
System.setProperty("spring.profiles.active", "test");
}
}
The problem here is that the static block does not appear to be executed in time. When running mvn clean install, the test gets an IllegalStateException because the ApplicationContext could not be loaded. I have verified that the static block gets executed, but it would appear that OldContext1 and OldContext2 (which are profile dependent) are already loaded at this time, which means it is too late.
The frustrating thing is that IntelliJ runs the tests just fine this way. Maven, however, does not. Is there a way to force these profiles while keeping it encapsulated? I've tried creating an intermediary context class, but it didn't solve the problem.
If we use the annotation #ActiveProfiles on the test class, it runs just fine but this kind of defeats the purpose. Naturally, we want to achieve the same in production and this means that if we cannot encapsulate the need for profiles, it needs to be configured in the web.xml.
If your configuration classes inherits of AbstractApplicationContext you can call:
getEnvironment().setActiveProfiles("your_profile");
For example:
public class TestContext extends AnnotationConfigWebApplicationContext {
public TestContext () {
getEnvironment().setActiveProfiles("test");
refresh();
}
}
Hope it helps.
It definietly seems that OldContext1 and OldContext2 are being class-loaded and initialized before the static block in OldComponents is executed.
Whilst I can't explain why there is a difference between your IDE and Maven (to do so would require some in-depth knowledge of some, if not all all, of : spring 3.x context initialization, maven surefire plugin, SpringJunit4ClassRunner and the internal IntelliJ test runner), can I recommend to try this?
#Configuration
#Import(value = {UseTestProfile.class, OldContext1.class, OldContext2.class})
public class OldComponents {
// moved the System.setProperty call to UseTestProfile.class
}
and
#Configuration
public class UseTestProfile {
static {
System.setProperty("spring.profiles.active", "test");
}
}
If I am understanding your problem correctly, class UseTestProfile should be loaded first (you might want to investigate a way to guarantee this?) and the other two classes in the import list should have the system setting they need to initialize properly.
Hope this helps...
You need make sure, environment takes effect at first.This is how I do:
#Component
public class ScheduledIni {
#Autowired
private Environment env;
#PostConstruct
public void inilizetion() {
String mechineName = env.getProperty("MACHINE_NAME");
if ("test".equals(mechineName) || "production".equals(mechineName) {
System.setProperty("spring.profiles.default", "Scheduled");
System.setProperty("spring.profiles.active", "Scheduled");
}
}
}
In scheduler add annotation Prodile and DependsOn to make it work.
#DependsOn("scheduledIni")
#Profile(value = { "Scheduled" })
#Component
Use #profile annotation in the class to load the configuration like below
#Configuration
#Profile("test")
public class UseTestProfile {
}
and set the value for the property spring.profiles.active either in property file or as a runtime argument

Categories

Resources