Spring MVC Pass Bean Object to RestController from embedded Tomcat - java

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);
}
}

Related

Spring ApplicationContext.getBean returns wrong class

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.

Spring boot: How to pass a command line argument into an annotation value?

Here is what I'm trying to do:
#SpringBootApplication public class App {
public static final String NAME;
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
#Autowired public App(ApplicationArguments arguments) {
NAME = arguments.getSourceArgs()[0]; // ERROR (1)
}
#GetMapping("/" + NAME) public void test() { // ERROR (2)
return NAME;
}
}
The code doesn't work as written because (1) NAME cannot be assigned, and (2) annotation value for #GetMapping must be a constant expression.
I just want #GetMapping to use a value based on a command line argument. How can this be done?
in the first request. spring mvc will init the resource. so your put your dynamic url in haddlermapping.
we need three classes. MyController MyDispatcherServlet DispatcherServletCustomConfiguration
MyController.java
#Component(value="MyController")
public class MyController {
#Autowired
ClaimService claimService;
public ResponseEntity<HttpStatus> insertClaim() {
return new ResponseEntity<>(HttpStatus.OK);
}
}
MyDispatcherServlet.java. after initStrategies.put, your dynamic url into handdlerMapping.
public class MyDispatcherServlet extends DispatcherServlet {
private String url;
public MyDispatcherServlet(String url) {
super();
this.url = url;
}
#Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
List<HandlerMapping> handlerMappings = getHandlerMappings();
for (HandlerMapping handlerMapping : handlerMappings) {
if (handlerMapping instanceof RequestMappingHandlerMapping) {
RequestMappingHandlerMapping requestMappingHandlerMapping = ((RequestMappingHandlerMapping) handlerMapping);
RequestMappingInfo.Builder n = RequestMappingInfo
.paths(url)
.methods(RequestMethod.GET);
try {
Method method = MyController.class.getDeclaredMethod("insertClaim");
requestMappingHandlerMapping.registerMapping(n.build(), "MyController", method);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
}
}
DispatcherServletCustomConfiguration.java
#Configuration
public class DispatcherServletCustomConfiguration {
#Value("${myUrl}")
private String url;
#Bean
public DispatcherServlet dispatcherServlet() {
return new MyDispatcherServlet(url);
}
}
run with the command java -jar stackoverflow-1.0-SNAPSHOT.jar --myUrl=abcd

Vaadin+Spring (without SpringBoot) with JavaConfig

i'm trying to combine vaadin with spring (without spring-boot) and java-annotation based configuration for the spring part.
The autowiring seems to work on the vaadin-ui part but not in "custom-ui classes" (e.g. "public class LoginScreen extends CustomComponent"). I'm getting an NPE or a null-object on SysOut.
Further i noticed that "#ComponentScan(basePackages={"net.myapp"})" is not scanning for beans. The only way to declare beans is in the CustomConfiguration itself.
XML-Configuration is not something i prefer.
I'm following this Tutorial: Link
CustomConfiguration.java
#Configuration
#ComponentScan(basePackages={"net.myapp"})
#EnableVaadin
public class CustomConfiguration {
// this is working but i want to use componentscan!
#Bean
public String test() {
return "test...";
}
#Bean
public TestBean testBean() {
return new TestBean();
}
#Bean
public LoginScreen loginScreenBean() {
return new LoginScreen();
}
}
SpringVaadinServlet.java
#WebServlet(value = "/*", asyncSupported = true)
#VaadinServletConfiguration(productionMode = false, ui = Application.class)
#SuppressWarnings("serial")
public class SpringVaadinServlet extends VaadinServlet implements SessionInitListener {
#Autowired
protected VaadinUIProvider applicationProvider;
#Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
AutowireCapableBeanFactory ctx = ((ApplicationContext)
getServletContext().getAttribute("applicationContext")).getAutowireCapableBeanFactory();
ctx.autowireBean(this);
}
#Override
protected void servletInitialized() {
getService().addSessionInitListener(this);
}
#Override
public void sessionInit(SessionInitEvent event) throws ServiceException {
event.getSession().addUIProvider(applicationProvider);
}
}
VaadinUIProvider.java
#SpringComponent
#SuppressWarnings("serial")
public class VaadinUIProvider extends UIProvider {
#Autowired
ApplicationContext applicationContext;
#Override
public Class<? extends UI> getUIClass(UIClassSelectionEvent event) {
return Application.class;
}
#Override
public UI createInstance(UICreateEvent event) {
UI instance = new Application();
System.out.println("applicationContext is null? " + applicationContext);
applicationContext.getAutowireCapableBeanFactory().autowireBean(instance);
return instance;
}
}
SpringApplicationContextListener.java
#WebListener
public class SpringApplicationContextListener implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent sce) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(CustomConfiguration.class);
sce.getServletContext().setAttribute("applicationContext", applicationContext);
}
#Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
}
Application.java
#Theme("mytheme1")
#SpringUI
#SuppressWarnings("serial")
public class Application extends UI {
#Autowired
private TestBean testBean;
#Autowired
private String test;
#Override
protected void init(VaadinRequest vaadinRequest) {
// working
System.out.println("init testBean: " + testBean);
System.out.println("init test: " + test);
Window window = new Window();
window.setContent(new LoginScreen());
window.setClosable(false);
window.setWidth("400px");
window.setHeight("280px");
window.setModal(true);
window.setDraggable(false);
window.setResizable(false);
window.center();
addWindow(window);
setSizeFull();
}
}
And the following "custom-ui class"
LoginScreen.java
#UIScope
#SuppressWarnings("serial")
public class LoginScreen extends CustomComponent {
public static final String VIEW_NAME = "";
final FormLayout layout = new FormLayout();
TextField userName = new TextField();
TextField passWord = new TextField();
Button submit = new Button("Submit");
#Autowired
private TestBean testBean;
#Autowired
private String test;
public LoginScreen() {
userName.setCaption("Benutzername:");
passWord.setCaption("Passwort:");
// not working (null)
System.out.println("loginscreen test: " + testBean);
System.out.println("loginscreen test: " + test);
setSizeFull();
}
}
I'd appreciate some help...
window.setContent(new LoginScreen());
Spring should create LoginScreen if you want that #Autowired annotated fields become injected.
Just inject the LoginScreen instance in your Application class

FactoryBeanNotInitializedException: Cannot determine target class for proxy

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();
}
}
}

Spring Config from XML to Java not working

I cannot seem to get simple Spring application to work with JavaConfig.
public class WebApp extends AbstractAnnotationConfigDispatcherServletInitializer {
private static final Logger logger = Logger.getLogger(WebApp.class);
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[0];
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[]{ WebAppConfig.class };
}
#Override
protected String[] getServletMappings() {
return new String[]{ "/" };
}
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
logger.debug("onStartup");
super.onStartup(servletContext);//MUST HAVE
servletContext.setInitParameter("defaultHtmlEscape", "true");
}
#Configuration
#EnableWebMvc
#ComponentScan("com.doge.controller")
public static class WebAppConfig extends WebMvcConfigurerAdapter {
}
}
And controller:
package com.doge.controller;
#RestController
public class HelloController {
#RequestMapping("/")
public String sayHello() {
System.out.println("something");
return "index";
}
}
I always get 404 on "localhost:8080/Build" nor "localhost:8080".
Nothing is ever logged nor printed, just "INFO: Server startup in 538 ms".
There are few options of initialize Spring web application. The easiest is like below:
public class SpringAnnotationWebInitializer extends AbstractContextLoaderInitializer {
#Override
protected WebApplicationContext createRootApplicationContext() {
AnnotationConfigWebApplicationContext applicationContext =
new AnnotationConfigWebApplicationContext();
applicationContext.register(WebAppConfig.class);
return applicationContext;
}
}
Other options can be found here: http://www.kubrynski.com/2014/01/understanding-spring-web-initialization.html

Categories