I want to setup an object pool in my spring application with annotation only.
I started out with this example taken from Spring docs: http://docs.spring.io/spring/docs/2.5.x/reference/aop-api.html#aop-ts-pool.
Here is how I translate the XML configuration:
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class SpringObjectPoolTest {
public static void main(String[] args) throws Exception {
context = new SpringApplicationBuilder(SpringObjectPoolTest.class) //
.addCommandLineProperties(false) //
.web(false) //
.headless(false) //
.registerShutdownHook(true) //
.application() //
.run();
context.getBean(SpringObjectPoolTest.class).go();
}
#Resource(name = "pfb")
private FactoryBean<MyTask> pool;
#Resource(name="pool")
private TargetSource targetSource;
private static ConfigurableApplicationContext context;
#Bean(name = "task")
#Scope("prototype")
public MyTask createNewTask() {
return new MyTask();
}
#Bean(name = "pool")
public CommonsPoolTargetSource setupObjectPool() {
CommonsPoolTargetSource pc = new CommonsPoolTargetSource();
pc.setMaxSize(25);
pc.setTargetBeanName("task");
return pc;
}
#Bean(name = "pfb")
public ProxyFactoryBean createProxyFactoryBean() {
ProxyFactoryBean pfb = new ProxyFactoryBean();
pfb.setTargetSource(targetSource);
return pfb;
}
private void go() {
try {
pool.getObject().speak();
} catch (Exception e) {
e.printStackTrace();
}
}
}
However I get this exception:
org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'pfb':
org.springframework.beans.factory.FactoryBeanNotInitializedException:
Cannot determine target class for proxy
You are a bit over engineering this. Spring already knows how to inject a proxied MyTask, there is no need to have a FactoryBean<MyTask> or to call getObject() on the pool. In "pooledTask" below Spring knows that by injecting a ProxyFactoryBean ("pfb") it will actually inject the instance that factory bean creates, not the factory bean itself. Here's how I'd do it:
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class SpringObjectPoolTest {
public static void main(String[] args) throws Exception {
context = new SpringApplicationBuilder(SpringObjectPoolTest.class) //
.addCommandLineProperties(false) //
.web(false) //
.headless(false) //
.registerShutdownHook(true) //
.application() //
.run();
context.getBean(SpringObjectPoolTest.class).go();
}
private static ConfigurableApplicationContext context;
#Resource(name = "pfb")
private MyTask pooledTask;
#Resource(name="pool")
private CommonsPoolTargetSource targetSource;
#Bean(name = "task")
#Scope("prototype")
public MyTask createNewTask() {
return new MyTask();
}
#Bean(name = "pool")
public CommonsPoolTargetSource setupObjectPool() {
CommonsPoolTargetSource pc = new CommonsPoolTargetSource();
pc.setMaxSize(25);
pc.setTargetBeanName("task");
return pc;
}
#Bean(name = "pfb")
public ProxyFactoryBean createProxyFactoryBean() {
ProxyFactoryBean pfb = new ProxyFactoryBean();
pfb.setTargetSource(setupObjectPool());
return pfb;
}
private void go() {
try {
pooledTask.speak();
// getting another object from pool
MyTask someOtherTask = (MyTask) targetSource.getTarget();
// returning the object to the pool
targetSource.releaseTarget(someOtherTask);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Related
I am building a REST API into an existing Spring Java application and I am not sure how to pass the Bean to the Rest Controller from the Main part of the app.
I would like to be able to have the Bean IDao created with its database instance passed into the UserController so it can be used as shown below.
As is, it is not able to autowire the Bean into the UserController. If I change the ComponentScan to include the main package it will autowire but not without ending up in and infinite Bean creation loop. What am I doing wrong?
package com.app.main;
public class App {
private static AnnotationConfigApplicationContext ctx;
public static void main(String[] args) throws Exception {
ctx = new AnnotationConfigApplicationContext(RestApiConfig.class);
}
}
package com.app.main;
#Configuration
public class RestApiConfig {
#Bean
public IDao<User> userDao(Database database) {
return new DatabaseDao<>(database, User.class);
}
#Bean
public Database database() {
return new Database();
}
#Bean
public RestApi restApi(IDao<User> userDao) {
return new RestApi(userDao);
}
package com.app.rest;
public class RestApi {
private final int PORT = 8080;
private final IDao<User> userDao;
public RestApi( IDao<User> userDao) {
this.userDao = userDao;
run();
}
public void run() {
String contextPath = "/api";
String webappDir = new File("src/main/webapp").getAbsolutePath();
Tomcat tomcat = new Tomcat(); // Tomcat 9.x.x
tomcat.setPort(PORT);
tomcat.getConnector(); // Trigger the creation of the default connector
Context context = tomcat.addWebapp(contextPath, webappDir);
try {
tomcat.start();
} catch (LifecycleException e) {
e.printStackTrace();
}
tomcat.getServer().await();
}
}
package com.app.rest;
#Configuration
#EnableWebMvc
#ComponentScan({"com.app.rest"})
public class RestApiServletConfig {
}
package com.app.rest;
public class RestApiServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
// TODO Auto-generated method stub
return null;
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{ RestApiServletConfig.class };
}
#Override
protected String[] getServletMappings() {
return new String[]{ "/" };
}
}
package com.app.rest;
#RestController
public class UserController {
private final IDao<User> repository;
UserController(IDao<User> repository) {
this.repository = repository;
}
#GetMapping("/users/{id}")
public User userById(#PathVariable Long id) {
return repository.get(id);
}
}
I am trying to use the value of a bean at the main class but I am getting an error because it can't be used in a static context.
#SpringBootApplication
public class MigrationsApplication implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(MigrationsApplication.class);
#Autowired
private UrlInterface url;
#Autowired
private UsernameInterface username;
#Autowired
private PasswordInterface password;
#Autowired
private ChangelogInterface changelog;
#Bean
public UrlInterface getUrl(#Value("${spring.datasource.url}") String dbUrl) {
return () -> dbUrl;
}
#Bean
public UsernameInterface getUsername(#Value("${spring.datasource.username}") String dbUsername) {
return () -> dbUsername;
}
#Bean
public PasswordInterface getPassword(#Value("${spring.datasource.password}") String dbPassword) {
return () -> dbPassword;
}
#Bean
public ChangelogInterface getChangelog(#Value("${spring.liquibase.change-log}") String changelogPath) {
return () -> changelogPath;
}
public static void main(String[] args) throws DatabaseException {
if (args.length < 2) {
logger.error("Please provide the desired command and the properties file");
System.exit(-1);
}
switch (args[0]) {
case "run":
ConfigurableApplicationContext ctx = SpringApplication.run(MigrationsApplication.class, args);
int exitCode = SpringApplication.exit(ctx, () -> 0);
logger.info("All migrations were finished with success");
System.exit(exitCode);
break;
case "rollback":
/*Connection connection = null;
try {
connection = openConnection(url.getUrl(),"","");
} catch (SQLException throwables) {
throwables.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
case "release":
logger.warn("RELEASE");
break;
default:
logger.error("Invalid command");
break;
}
}
#Override
public void run(String... args) throws Exception {
}
}
How can I turn around this? I can't move the code to the run method because even though it's empty it stills updates my database.
Why does the method run updates my database even if it is empty?
Is it even possible to use properties values in the Main class?
Only after SpringApplication.run is executed, the ApplicationContext instance is created and then the Beans would be loaded and are available.
To access a Bean in static main method, you could fetch it directly from the ApplicationContext. Maybe something like:
ApplicationContext ctx = SpringApplication.run(MigrationsApplication.class, args);
UrlInterface url = (UrlInterface) ctx.getBean("getUrl");
For using #Autowired with a static method. You need to use static variable and inject it with #Autowired on the constructor. See this answer for example
I've been beating my head over this and I just can't figure out what's wrong.
I have a Spring app which uses ApplicationContext.getBean() to retrieve 2 similar classes. I'm getting the wrong instance class from the bean lookup.
Here's ApplicationContext class:
public class DomainRegistryCab {
private static ApplicationContext applicationContext;
private static ApplicationContext createApplicationContext() {
return new AnnotationConfigApplicationContext( CaBridgeDomainServiceConfig.class );
}
public static CertificateProductApplicationService certificateProductAppService() {
var service = BeanFactoryAnnotationUtils.qualifiedBeanOfType(
applicationContext().getAutowireCapableBeanFactory(),
CertificateProductApplicationService.class,
CaBridgeDomainServiceConfig.CERTIFICATE_PRODUCT_APP_SERVICE);
// var service = applicationContext().getBean(
// CaBridgeDomainServiceConfig.CERTIFICATE_PRODUCT_APP_SERVICE,
// CertificateProductApplicationService.class);
// var service = applicationContext().getBean(CertificateProductApplicationService.class);
validateDataSourceIs(DataSource.ProductDataStore, service.dataSource());
return service;
}
public static CertificateProgramApplicationService certificateProgramAppService() {
var service = BeanFactoryAnnotationUtils.qualifiedBeanOfType(
applicationContext().getAutowireCapableBeanFactory(),
CertificateProgramApplicationService.class,
CaBridgeDomainServiceConfig.CERTIFICATE_PROGRAM_APP_SERVICE);
// var service = applicationContext().getBean(
// CaBridgeDomainServiceConfig.CERTIFICATE_PROGRAM_APP_SERVICE,
// CertificateProgramApplicationService.class);
// service = applicationContext().getBean(CertificateProgramApplicationService.class);
validateDataSourceIs(DataSource.ProgramDataStore, service.dataSource());
return service;
}
Here is CaBridgeDomainServiceConfig:
#Configuration
#ComponentScan(basePackageClasses = { HibernateConfigurationMarker.class })
public class CaBridgeDomainServiceConfig {
public static final String CERTIFICATE_PRODUCT_APP_SERVICE = "certificateProductAppService";
public static final String CERTIFICATE_PROGRAM_APP_SERVICE = "certificateProgramAppService";
#Bean(name= CERTIFICATE_PRODUCT_APP_SERVICE)
public CertificateProductApplicationService certificateProductAppService() {
return new CertificateProductApplicationServiceCabImpl();
}
#Bean(name= CERTIFICATE_PROGRAM_APP_SERVICE)
public CertificateProgramApplicationService certificateProgramAppService() {
return new CertificateProgramApplicationServiceCabImpl();
}
}
public interface CertificateProductApplicationService extends CertificateApplicationService {
}
public interface CertificateProductApplicationService extends CertificateApplicationService {
}
public interface CertificateApplicationService {
}
Using the above classes if I call DomainRegistryCab.certificateProductAppService() I get an instance of CertificateProgramApplicationService not CertificateProductApplicationService.
I get similar results if I use this method:
public static CertificateProductApplicationService certificateProductAppService() {
var service = applicationContext().getBean(
CaBridgeDomainServiceConfig.CERTIFICATE_PRODUCT_APP_SERVICE,
CertificateProductApplicationService.class);
validateDataSourceIs(DataSource.ProductDataStore, service.dataSource());
return service;
}
I've also tried having the #Bean methods return the implementation classes and the ApplicationContext().getBean() to request the implementation classes instead of the interfaces:
public class DomainRegistryCab {
private static ApplicationContext applicationContext;
private static ApplicationContext createApplicationContext() {
return new AnnotationConfigApplicationContext( CaBridgeDomainServiceConfig.class );
}
public static CertificateProductApplicationService certificateProductAppService() {
var service = applicationContext().getBean(CertificateProductApplicationServiceCabImpl.class);
validateDataSourceIs(DataSource.ProductDataStore, service.dataSource());
return service;
}
public static CertificateProgramApplicationService certificateProgramAppService() {
var service = applicationContext().getBean(CertificateProgramApplicationServiceCabImpl.class);
validateDataSourceIs(DataSource.ProgramDataStore, service.dataSource());
return service;
}
public static ApplicationContext applicationContext() {
if (applicationContext == null)
applicationContext = createApplicationContext();
return applicationContext;
}
}
#ComponentScan(basePackageClasses = { HibernateConfigurationMarker.class })
public class CaBridgeDomainServiceConfig {
#Bean(name= CERTIFICATE_PRODUCT_APP_SERVICE)
public CertificateProductApplicationServiceCabImpl certificateProductAppService() {
return new CertificateProductApplicationServiceCabImpl();
}
#Bean(name= CERTIFICATE_PROGRAM_APP_SERVICE)
public CertificateProgramApplicationServiceCabImpl certificateProgramAppService() {
return new CertificateProgramApplicationServiceCabImpl();
}
}
This code results in spring not finding the implementation classes at all:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'cmb.cabridge.application.cert.CertificateProductApplicationServiceCabImpl' available
I was eventually able to get things to work using the applicationContext().getBean("beanName", CertificateProductApplicationService.class). The problem was deeper in my code in the CertificateProductApplicationServiceCabImpl class which used and returned the wrong datasource.
#Component
public class SampleApplication {
private static final Logger LOGGER = Logger.Factory.getLogger();
private KafkaProducer<String, String> producer = null;
public SampleApplication() {}
#Async(value = "audit-task-executor")
public void sendMessageToExternalKafka(String casterMessage) throws IOException {}
}
In spring bean.xml
<task:executor id="audit-task-executor"
pool-size="1"
queue-capacity="1"
rejection-policy="DISCARD_OLDEST"/>
I want to write a test to mock it, but not able to do it. Its throwing following exception.
Initialization of bean failed; nested exception is java.lang.NoClassDefFoundError: org/springframework/objenesis/SpringObjenesis
My test configuration:
#Bean(name = "producerExt")
FactoryBean sampleApplication() {
return new AbstractFactoryBean() {
#Override
public Class getObjectType() {
return SampleApplication.class;
}
#Override
protected SampleApplication createInstance() {
return mock(SampleApplication.class);
}
};
}
#Bean(name = "audit-task-executor")
public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
return mock(ThreadPoolTaskExecutor.class);
}
Implemented FactoryBean to avoid invocation of #PostConstruct
I have a class annotated with #Component which is use to initialze application.yml config properties. Service classe is using configuration property. But sometime my Service class instance created before the Configuration class and I get null property value in service class, Its random not specific pattern.
Configuration Initializer class..
#Component
public class ConfigInitializer implements InitializingBean {
private static final Logger log = LoggerFactory.getLogger(ConfigInitializer.class);
#Autowired
ProxyConfig proxyConfig;
/*#PostConstruct
public void postConstruct(){
setProperties();
}
*/
#Override
public void afterPropertiesSet() {
setProperties();
}
private void setSystemProperties(){
log.debug("Setting properties...");
Properties props = new Properties();
props.put("PROXY_URL", proxyConfig.getProxyUrl());
props.put("PROXY_PORT", proxyConfig.getProxyPort());
System.getProperties().putAll(props);
}
}
#Component
#ConfigurationProperties(prefix = "proxy-config")
public static class ProxyConfig {
private String proxyUrl;
private String proxyPort;
public String getProxyUrl() {
return proxyUrl;
}
public void setProxyUrl(String proxyUrl) {
this.proxyUrl = proxyUrl;
}
public String getProxyPort() {
return proxyPort;
}
public void setProxyPort(String proxyPort) {
this.proxyPort = proxyPort;
}
}
Service Class..
#Service("receiverService")
public class ReceiverService {
private static final Logger logger = LoggerFactory.getLogger(ReceiverService.class);
private ExecutorService executorService = Executors.newSingleThreadExecutor();
#Autowired
public ReceiverService() {
initClient();
}
private void initClient() {
Future future = executorService.submit(new Callable(){
public Object call() throws Exception {
String value = System.getProperty("PROXY_URL"); **//Here I am getting null**
logger.info("Values : " + value);
}
});
System.out.println("future.get() = " + future.get());
}
}
Above Service class get null values String value = System.getProperty("PROXY_URL")
When I use #DependsOn annotation on Service class, it works fine.
In my little knowledge, I know Spring does not have specific order of bean creation.
I want to know If I use #Configuration instead of #Component on ConfigInitializer class like below, Will spring initialize ConfigInitializer
class before other beans ?.
#Configuration
public class ConfigInitializer implements InitializingBean {
//code here
}