I have a Spring Boot application with the following structure:
com.test (Root Class)
com.test.jpa (JPA Entities and Repositories)
com.test.netty (netty TCP server)
The root class is:
#ComponentScan(basePackages = {"com.test"})
//#EnableJpaRepositories
//#EntityScan
public class MyApplication {
...
The Netty Server:
package com.test.netty;
#Service
#Slf4j
public class NettyServer {
private EventLoopGroup boss = new NioEventLoopGroup();
private EventLoopGroup work = new NioEventLoopGroup();
#PostConstruct
public void start() {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(boss, work).channel(NioServerSocketChannel.class).localAddress(new InetSocketAddress(port))
// .option(ChannelOption.SO_BACKLOG, 1024)
.handler(new LoggingHandler(LogLevel.INFO)).childOption(ChannelOption.SO_KEEPALIVE, true)
.childOption(ChannelOption.TCP_NODELAY, true).childHandler(new ServerChannelInit());
try {
ChannelFuture future = bootstrap.bind().sync();
if (future.isSuccess()) {
log.info("Netty Server Started!");
}
} catch (InterruptedException ie) {
log.error("Error Initializing Netty Server. Error: " + ie.getMessage());
}
}
#PreDestroy
public void destroy() throws InterruptedException {
boss.shutdownGracefully().sync();
work.shutdownGracefully().sync();
log.info("Netty Server Shut Down!");
}
and:
public class ServerChannelInit extends ChannelInitializer<SocketChannel>{
#Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast("mainHandler", new ServiceHandler());
}
and:
package com.test.netty;
#Component
public class ServiceHandler extends ChannelInboundHandlerAdapter {
private SomeEntity en;
#Autowired
SomeRepository sr;
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// Read data and persist some entitys using injected repository
And repository:
package com.test.jpa;
//#Repository
public interface SomeRepository extends JpaRepository<SomeEntity, BigInteger> {
}
The problem is: Repository is not injected into com.test.netty classes. I use it in root class and in JUnit tests without any problem.
I added #Repository to the repository and also added repo packages in #EnableJPARepositories but nothing changed.
Any ideas?
Well, if you're creating an instance of ServiceHandler yourself rather than using the bean instance Spring creates for you, of course no dependency injection will be performed. You need to inject the ServiceHandler bean into ServerChannelInit as well as make ServerChannelInit a #Component itself:
#Component
public class ServerChannelInit extends ChannelInitializer<SocketChannel>{
private final ServiceHandler handler;
public ServerChannelInit(ServiceHandler handler) {
this.handler = handler;
}
#Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast("mainHandler", handler);
}
...
}
and then inject ServerChannelInit into NettyServer:
#Service
#Slf4j
public class NettyServer {
private final ServerChannelInit channelInit;
public NettyServer(ServerChannelInit channelInit) {
this.channelInit = channelInit;
}
private EventLoopGroup boss = new NioEventLoopGroup();
private EventLoopGroup work = new NioEventLoopGroup();
#PostConstruct
public void start() {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(boss, work).channel(NioServerSocketChannel.class).localAddress(new InetSocketAddress(port))
// .option(ChannelOption.SO_BACKLOG, 1024)
.handler(new LoggingHandler(LogLevel.INFO)).childOption(ChannelOption.SO_KEEPALIVE, true)
.childOption(ChannelOption.TCP_NODELAY, true).childHandler(channelInit);
...
}
I just executed with the followings, just add #SpringBootApplication in your main class. Uncomment #Repository
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
#Repository
public interface SomeRepository extends JpaRepository<Person, BigInteger> {
void foo();
}
#Component
public class SampleRepo implements SomeRepository{
#Override
public void foo() {
System.out.println("Called...." );
}
}
#RestController
public class ServiceHandler {
#Autowired
private SomeRepository sr;
#GetMapping("/hello")
public void call(){
sr.foo();
}
}
It works!
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 have a REST-Backend created with JHipster. There are different exception-classes in the service layer and the web-rest layer. This service-exceptions are translated by an ExceptionTranslator which implements the ProblemHandling interface from org.zalando.problem.spring.web.advice
I have the following ExceptionTranslator:
#ControllerAdvice
public class ExceptionTranslator implements ProblemHandling, SecurityAdviceTrait {
#Override
public ResponseEntity<Problem> process(#Nullable ResponseEntity<Problem> entity, NativeWebRequest request) {
//generated by jHipster
}
#ExceptionHandler(HouseWithoutOwnerServiceException.class)
public ResponseEntity<Problem> handleHouseWithoutOwnerException(HouseWithoutOwnerServiceException ex, NativeWebRequest request) {
return create(new HouseWithoutOwnerException(), request);
}
}
The service-exception class:
public class HouseWithoutOwnerServiceException extends RuntimeException {
public HouseWithoutOwnerServiceException() {
super("House without owner!");
}
}
The rest-error class:
public class HouseWithoutOwnerException extends AbstractThrowableProblem {
private static final long serialVersionUID = 1L;
public HouseWithoutOwnerException() {
super(ErrorConstants.HOUSE_WITHOUT_OWNER_TYPE, "House does not have an owner", Status.CONFLICT);
}
}
In my test the HouseWithoutOwnerServiceException is thrown but not translated into a HouseWithoutOwnerException:
#SpringBootTest(classes = HouseApp.class)
public class HouseControllerIT {
#Autowired
private MappingJackson2HttpMessageConverter jacksonMessageConverter;
#Autowired
private PageableHandlerMethodArgumentResolver pageableArgumentResolver;
#Autowired
private ExceptionTranslator exceptionTranslator;
private MockMvc restHouseMockMvc;
#BeforeEach
public void setup() {
HouseController houseController = new HouseController(houseService);
this.restHouseMockMvc = MockMvcBuilders.standaloneSetup(houseController)
.setCustomArgumentResolvers(pageableArgumentResolver)
.setControllerAdvice(exceptionTranslator)
.setMessageConverters(jacksonMessageConverter)
.build();
}
#Test
#Transactional
public void createHouseWithoutExistingOwner() throws Exception {
HouseDTO houseDTO = createHouseDTOWithoutOwner();
houseDTO.setOwnerId(ownerId + 1); //not existing
restHouseMockMvc.perform(post("/api/v1/houses")
.contentType(TestUtil.APPLICATION_JSON_UTF8)
.content(TestUtil.convertObjectToJsonBytes(houseDTO)))
.andExpect(status().isConflict());
}
}
Therefore I always get 500 Internal Server Error instead of 409 Conflict. I debugged it already and the method in the ExceptionTranslator is not entered.
I followed guide here, and I was successfully able to configure a producer on my bean endpoint like this:
#Produce( uri = "activemq:foo" )
private MyListener myListener;
MyListener is:
public interface MyListener {
#InOnly
public void send( String message );
}
and my bean:
public class MyBeanEndpoint {
#Produce( uri = "activemq:foo" )
private MyListener myListener;
#Handler
public void doSomething( final Object body ) {
...
}
public void setMyListener( final MyListener myListener ) {
this.myListener = myListener;
}
Now, how can I test this?
I mean: my test extends CamelTestSupport and I configured my routes with
#Override
public RouteBuilder createRouteBuilder() {
return new RouteBuilder() { ... }
That is: I've reproduced camel context, but I've NO spring context configured and I want (if possible) to avoid instantiating it.
How can I mock producer or make Camel instantiate and inject this bean into my bean endpoint?
What is the best way to test such situation using Apache Camel features like CamelTestSupport and similar utilities?
My reference test is:
public class Test extends CamelTestSupport {
private static BeanEndpoint beanEndpoint
#BeforeClass
public static void init() {
beanEndpoint.setActivemqMyListener( ??? );
}
#Override
public CamelContext createCamelContext() {
context = new DefaultCamelContext();
context.addComponent( "activemq", new SedaComponent() );
return context;
}
#Override
public RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from( "activemq:foo" )
.to( "mock:out" );
}
};
}
#Test
public void testFooQueue() throws Exception {}
Let Camel create your bean, then the various dependency injection and whatnot is configured for you.
private static BeanEndpoint beanEndpoint
...
beanEndpoint = camelContext.getInjector().newInstance(BeanEndpoint.class);
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
I have this service bean with a sync method calling the internal async method:
#Service
public class MyService {
public worker() {
asyncJob();
}
#Async
void asyncJob() {
...
}
}
The trouble is that the asyncJob is not really called in async way.
I found that this doesn't work because an internal call skips the AOP proxy.
So I try to self-refer the bean:
#Service
public class MyService {
MyService mySelf;
#Autowired
ApplicationContext cnt;
#PostConstruct
public void init() {
mySelf=(MyService)cnt.getBean("myService");
}
public void worker() {
mySelf.asyncJob();
}
#Async
void asyncJob() {
...
}
}
It fails. Again no async call.
So I tried to divide it in two beans:
#Service
public class MyService {
#Autowired
MyAsyncService myAsyncService;
public void worker() {
myAsyncService.asyncJob();
}
}
#Service
public class MyAsyncService {
#Async
void asyncJob() {
...
}
}
Fails again.
The only working way is to call it from a Controller Bean:
#Controller
public class MyController {
#Autowired
MyAsyncService myAsyncService;
#RequestMapping("/test")
public void worker() {
myAsyncService.asyncJob();
}
}
#Service
public class MyAsyncService {
#Async
public void asyncJob() {
...
}
}
But in this case it is a service job. Why I cannot call it from a service?
Found a really nice way to solve this (with java8) in the case where you have a lot of various things you want to both sync and async. Instead of creating a separate XXXAsync service for each 'synchronous' service, create a generic async service wrapper:
#Service
public class AsyncService {
#Async
public void run(final Runnable runnable) {
runnable.run();
}
}
and then use it as such:
#Service
public class MyService {
#Autowired
private AsyncService asyncService;
public void refreshAsync() {
asyncService.run(this::refresh);
}
public void refresh() {
// my business logic
}
public void refreshWithParamsAsync(String param1, Integer param2) {
asyncService.run(() -> this.refreshWithParams(param1, param2));
}
public void refreshWithParams(String param1, Integer param2) {
// my business logic with parameters
}
}
I solved the third method (divide it in two beans) changing the async method's access modifier to public:
#Service
public class MyService {
#Autowired
MyAsyncService myAsyncService;
public void worker() {
myAsyncService.asyncJob();
}
}
#Service
public class MyAsyncService {
#Async
public void asyncJob() { // switched to public
...
}
}
In my case, it was easier to remove the #Async annotation and use the taskExecutor directly to submit my task:
Before
#Async("taskExecutor")
private Future<U> executerEnAsync(
final T pInput) {
final U resultat = this.appelerBS(pInput);
return new AsyncResult<U>(resultat);
}
After
#Autowired
private AsyncTaskExecutor taskExecutor;
private Future<U> executerEnAsync(
final T pInput) {
final Future<U> future = taskExecutor.submit(new Callable<U>() {
#Override
public U call() {
final U resultat = appelerBS(pInput);
return resultat;
}
});
return future;
}