I am new to use Struts 2 Framework.
I need to use DataSource Object in Struts Action Class.
My platform is Tomcat 8 (Servlet 3.1) and I set Resource in context.xml.
I can inject Container managed DataSource Object in a servlet by using #Resource annotation.
I'd tried in this way.
I create a ServletContextListener and inject DataSource in this listener.
I set this datasource to application scope object in contextInitialized method.
#WebListener
public class ResourceListener implements ServletContextListener {
#Resource(name="jdbc/skill_db")
private DataSource ds;
public ResourceListener() { }
#Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("Start");
sce.getServletContext().setAttribute("Datasource", ds);
sce.getServletContext().setAttribute("dbConfigStream", sce.getServletContext().getResourceAsStream("/WEB-INF/database.properties"));
}
#Override
public void contextDestroyed(ServletContextEvent sce) { }
}
After that I access application scope and get this datasource from Struts Action methods.
public String welcome() {
Map<String, Object> application = ActionContext.getContext().getApplication();
DataSource ds = (DataSource) application.get("Datasource");
InputStream conf = (InputStream) application.get("dbConfigStream");
Model<Employee> empModel = new BaseModel<Employee>(Employee.class,
Employee::convert, ds, conf);
list = empModel.getAll();
return "welcome";
}
My question are :
Can I get DataSource object in a structs action object?
Is this way that I tried a correct way in struts?
I tried my requirements by Struts2-CDI Plugin
By using CDI I can inject my dependencies.
1. I edit POM of my project as follow.
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-cdi-plugin</artifactId>
<version>2.3.24</version>
</dependency>
<dependency>
<groupId>javax.enterprise</groupId>
<artifactId>cdi-api</artifactId>
<version>1.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.weld.servlet</groupId>
<artifactId>weld-servlet</artifactId>
<version>2.2.15.Final</version>
</dependency>
2. As I used Tomcat I need to add this codes to context.xml and web.xml to use CDI.
2.1 context.xml
<Resource name="BeanManager" auth="Container"
type="javax.enterprise.inject.spi.BeanManager"
factory="org.jboss.weld.resources.ManagerObjectFactory" />
2.2 web.xml
<resource-env-ref>
<resource-env-ref-name>BeanManager</resource-env-ref-name>
<resource-env-ref-type>javax.enterprise.inject.spi.BeanManager</resource-env-ref-type>
</resource-env-ref>
3. Produce Datasource
Inject directly DataSource object and ServletContext into ResourceProducer class. So that I don't need listener class to set DataSource to application scope and also don't need to access indirectly to servlet context object.
Using CDI make free the limitations of Struts.
#ApplicationScoped
public class ResourceProducer {
#Resource(name="jdbc/skill_db")
private DataSource datasource;
#Inject
private ServletContext servletContext;
#Produces
#DbResourse
public DataSource getDatasource() {
return datasource;
}
#Produces
#DbConfiguration
public InputStream getConfiguration() {
return servletContext.getResourceAsStream("/WEB-INF/database.properties");
}
}
4. Inject DataSource at Model Producer
#Inject
#DbResourse
private DataSource ds;
#Inject
#DbConfiguration
private InputStream dbConfig;
#Produces
#DataModel(Employee.class)
public Model<Employee> getEmployeeModel() {
return new BaseModel<Employee>(Employee.class, Employee::convert, ds, dbConfig);
}
5. Inject Model at Struts 2 Action Class
#Inject
#DataModel(Employee.class)
private Model<Employee> empModel;
public String welcome() {
list = empModel.getAll();
return "welcome";
}
Related
Need help
#Configuration
#EnableR2dbcRepositories(basePackages = "com.paymentservice.repository", databaseClientRef = "databaseClient")
public class PaymentR2dbcConfiguration extends AbstractR2dbcConfiguration {
#Value("${payment.data.mssql.host}")
private String host;
#Value("${payment.data.mssql.port}")
private int port;
#Value("${payment.data.mssql.database}")
private String database;
#Value("${payment.data.mssql.username}")
private String username;
#Value("${payment.data.mssql.password}")
private String password;
/**
* An implementation of {#link ConnectionFactory} for creating connections to
* a Microsoft SQL Server database using R2DBC.
*
* #return A factory for creating {#link Connection}s.
*/
#Override
public ConnectionFactory connectionFactory() {
return new MssqlConnectionFactory(
MssqlConnectionConfiguration.builder()
.host(host)
.port(port)
.database(database)
.username(username)
.password(password).build());
}
}
I m getting
quote The bean 'r2dbcDatabaseClient', defined in class path resource [org/springframework/boot/autoconfigure/data/r2dbc/R2dbcDataAutoConfiguration.class], could not be registered. A bean with that name has already been defined in class path resource [com/paymentservice/configurations/PaymentR2dbcConfiguration.class] and overriding is disabled.
I m using
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-r2dbc</artifactId>
<version>1.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-mssql</artifactId>
<version>0.8.4.RELEASE</version>
</dependency>
If you are using Spring Boot starter to configure R2dbc automatically.And you want to customize the R2dbc configuration via AbstractR2dbcConfiguration.
If you are using more than one connection in your application, and use this config to add another connection factory, try to add a name attribute to #Bean to identify multiple beans.
#Bean(name="myConn")
#Override
public ConnectionFactory connectionFactory() {}
If you want to override the default ConnectionFactory created by Spring Boot starter, add an extra #Primary on it.
#Bean
#Primary
#Override
public ConnectionFactory connectionFactory() {}
You have missed to annotate the method connectionFactory with Bean.
Check the documentation here
#Bean
#Override
public ConnectionFactory connectionFactory() {
return new MssqlConnectionFactory(
MssqlConnectionConfiguration.builder()
.host(host)
.port(port)
.database(database)
.username(username)
.password(password).build());
}
If the problem still persists in case you have any overriding bean defined in any of your configuration class, then you should use this property:
spring.main.allow-bean-definition-overriding=true
Starting in Spring 5.1, the BeanDefinitionOverrideException was introduced to allow developers to automatically throw the exception to prevent any unexpected bean overriding.
this is an open issue in Spring-boot and spring-data-r2dbc see post:
https://github.com/spring-projects/spring-data-r2dbc/issues/296
and https://github.com/spring-projects/spring-boot/issues/21586
The solution which works for me is to use default R2dbcDataAutoConfiguration and remove the custom configuration AbstractR2dbcConfiguration until Spring team fixed it.
Below properties are used to initialize the R2dbcDataAutoConfiguration
spring:
r2dbc:
url: r2dbc:mssql://xx.xxx.xxx.xxx:6515/*****
username: xxxx_xxxx
password: xxxxx
The second solution which works for me is to exclude R2dbcDataAutoConfiguration using below code.
#SpringBootApplication(exclude = { R2dbcDataAutoConfiguration.class,
R2dbcAutoConfiguration.class })
Is it ok to access a datasource in a Spring DAO in the following way (rather than configuring a bean and autowiring it):
#Repository
public class MyDAOImpl extends JdbcDaoSupport implements MyDAO {
#PostConstruct
private void initialize() {
JndiDataSourceLookup lookup = new JndiDataSourceLookup();
lookup.setResourceRef(true);
DataSource dataSource = lookup.getDataSource("jdbc/TWO");
super.setDataSource(dataSource);
}
public MyStuff getMyStuff(Long id) {
// getJdbcTemplate().query() etc
}
}
This works but is it ok or will this cause problems? Maybe create a connection leak or something else?
Thanks
(Edited for clarity)
My goal is to create a Webserver with Spring. It has to implement Multitenancy, which works great if you don't make it dynamic (adding, removing, changing). Is it possible to update the datasource bean in Spring?
My code:
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) throws IOException {
SpringApplication.run(MyApplication.class, args);
}
//Multitenancy
#Bean
public DataSource dataSource(){
//implements AbstractRoutingDataSource
CustomRoutingDataSource customDataSource = new CustomRoutingDataSource();
//logic here
return customDataSource;
}
}
What I've tried:
CustomRoutingDataSource c = context.getBean(CustomRoutingDataSource.class);
c.setTargetDataSources(CustomRoutingDataSource.getCustomDatasources());
which updates the bean(?) but doesn't update Spring's datasources, database connections are still missing if added with this method.
Simple solution for those with the same problem:
Add #RefreshScope
#Bean
#RefreshScope
public DataSource dataSource() {
CustomRoutingDataSource customDataSource = new CustomRoutingDataSource();
...
return customDataSource;
}
Add spring actuator endpoint in pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
POST to /actuator/refresh to update datasources!
Is there any way to startup a CDI Bean with the application?
I have this bean:
#ApplicationScoped
#Named
public class MyBean implements Serializable {
#Inject
private Helper helper;
#PostConstruct
public void init() {
helper.doThings();
}
}
I would like that this bean to start with the app, on deploy.
I'm using the weld-servlet and tomcat7:
<dependency>
<groupId>org.jboss.weld.servlet</groupId>
<artifactId>weld-servlet</artifactId>
<version>${weld-servlet.version}</version>
<scope>compile</scope>
</dependency>
Try:
public void startup(#Observes #Initialized
(ApplicationScoped.class) Object context){
//your code here
}
That will ensure the method is executed at startup, and start your bean.
Or you can use OmniFaces #Eager annotation.
Ensure that your tomcat7 variant supports CDI (See tomcat version comparison)
Then,
#Startup
#Singleton
public class MyBean {
#PostConstruct
private void init() {
// place your initialization code here
}
}
I'm using Spring 3.2.5 without full new JSR-356 WebSockets support.
I would like to have singleton-bean reference in my #ServerEndpoint WebSocket server, which is instantiated by servlet container itself, not in Spring context.
What is the clean way to do it?
My current solution: I made #Service singleton bean with instance in static field:
#Service
public class WebSocketSupportBean {
private volatile static WebSocketSupportBean instance = null;
public static WebSocketSupportBean getInstance() {
return instance;
}
public WebSocketSupportBean() {
instance = this;
}
and just getting it in #ServerEndpoint by static method, disconnecting user if null returned (if bean not jet created during server startup but user connects):
You can setup websockets with spring framework 3.x
I developed a small proof-of-concept application to demonstrate how, based on Rossen Stoyanchev's SpringConfiguration released with spring-core 4.0.
The application sets up a websocket server endpoint with uri /wstest which will use a #Autowired spring bean to select a greeting word and reply to a websocket message.
The websocket connection is initiated and messages sent by an html page (index.html) running in a browser that supports websockets.
The Endpoint registration is made by a ServletContextListener at context initialization and when the endpoint is instantiated it will be wired with spring:
#WebListener
public class MyApplication implements ServletContextListener {
private final static String SERVER_CONTAINER_ATTRIBUTE = "javax.websocket.server.ServerContainer";
#Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext container = sce.getServletContext();
final ServerContainer serverContainer = (ServerContainer) container.getAttribute(SERVER_CONTAINER_ATTRIBUTE);
try {
serverContainer.addEndpoint(new MyEndpointConfig(MyEndpoint.class, "/wstest"));
} catch (DeploymentException e) {
e.printStackTrace();
}
}
}
And the Endpoint is:
#Component
public class MyEndpoint extends Endpoint {
#Autowired
MyService myService;
#Override
public void onOpen(Session session, EndpointConfig config) {
session.addMessageHandler(new MyMessageHandler(session));
}
class MyMessageHandler implements MessageHandler.Whole<String> {
final Session session;
public MyMessageHandler(Session session) {
this.session = session;
}
#Override
public void onMessage(String message) {
try {
String greeting = myService.getGreeting();
session.getBasicRemote().sendText(greeting + ", got your message (" + message + "). Thanks ! (session: " + session.getId() + ")");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Checkout the full source and ready to run example on my Github page.
You have to add bean definition in the configuration of spring.
The solution i found to integrate JSR 356 websocket #ServerEndpoint is to turn off the Servlet container's scan for WebSocket endpoints by spring which can be done by registering #Bean in your Spring Configuration. By this spring not overrides normal JSR 356 websocket by spring STOMP websocket which is the part of the websocket.
#ServerEndpoint(value="/chatMessage")
public class ChatEndpoint{
// Normal websocket body goes here.
}
Adding Beans in your Configuration as:
#Configuration
public class WebsocketConfig{
#Bean
public ChatEndpoint chatEndpoint(){
return new ChatEndpoint();
}
// main one is ServerEndpointExporter which prevents Servlet container's scan for WebSocket
#Bean
public ServerEndpointExporter endpointExporter(){
return new ServerEndpointExporter();
}
}
This all done for you. But you should remove configurator = SpringConfigurator.class from #ServerEndpoint.
I am using Spring Websocket 4.0.0 and it works fine.
You can also see this Link.
If you alright then follow this Link also for concept.
Note that, Normally you should make websocket configuration separately from the main configuration of your spring.
Try
#ServerEndpoint(value = "/ws", configurator = SpringConfigurator.class)
And add maven dependency
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
</dependency>
You can make your #ServerEndpoint object extend SpringBeanAutowiringSupport. Then just make it aware of beans that gets constructed within a Spring-based web application this way:
#PostConstruct
public void init() {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
}
This way #Autowired annotation will worl correctly:
#Autowired MyService myService;
try this,it works for me
#Component
#ServerEndpoint(value = "/instantMessageServer",configurator = SpringConfigurator.class)
public class InstantMessageServer{
private static IChatService chatService;
#Autowired
public InstantMessageServer(IChatService chatService){
this.chatService = chatService;
}
public InstantMessageServer(){}
}
I found this solution on https://spring.io/blog/2013/05/23/spring-framework-4-0-m1-websocket-support
but there is one more glitch,the class annotated with #ServerEndpoint cant acquire httpsession with SpringConfigurator,there is no a override of method modifyhandler in it.Maybe we create a seperate Configurator extends SpringConfigurator and override that method would be a workaroud.
It is better to build a real-time web application with spring-websocket and messaging api,I think.
public class ModifiedServerEndpointConfigurator extends SpringConfigurator{
#Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
HttpSession httpSession = (HttpSession) request.getHttpSession();
sec.getUserProperties().put(HttpSession.class.getName(),httpSession);
super.modifyHandshake(sec, request, response);
}
}