I'm writing a client/server app and configuring it with Spring.
My client interface handles marshalling requests to the server and handling the responses.
At the moment, I have a factory that looks something like:
public class ClientFactory {
private ApplicationContext ctx;
public ClientFactory(){
ctx = new AnnotationConfigApplicationContext(MyConfig.class);
}
public MyClient(String host, int port){
MyClient client = ...
// create a connection to the server
return client;
}
}
Now, MyClient has a bunch of dependencies that I would like to inject, so I would like to create the MyClient instance using Spring and use #Inject annotations to inject the dependencies.
How do I pass the host/port as configuration metadata into the Spring configuration? If I can't what is the recommended alternative. I could do all the wiring myself, but then that is what Spring is for.
Jeff
You should check configuration part of the spring reference. For example you can create beans like this with spring 3.x.
#Configuration
// spring config that loads the properties file
#ImportResource("classpath:/properties-config.xml")
public class AppConfig {
/**
* Using property 'EL' syntax to load values from the
* jetProperties value
*/
private #Value("#{jetProperties['jetBean.name']}") String name;
private #Value("#{jetProperties['jetBean.price']}") Long price;
private #Value("#{jetProperties['jetBean.url']}") URL url;
/**
* Create a jetBean within the Spring Application Context
* #return a bean
*/
public #Bean(name = "jetBean")
JetBean jetBean() {
JetBean bean = new JetBeanImpl();
bean.setName(name);
bean.setPrice(price);
bean.setUrl(url);
return bean;
}
}
I solved this using a static configuration class.
public class ClientFactory {
private ApplicationContext ctx;
public ClientFactory(){
ctx = new AnnotationConfigApplicationContext(MyConfig.class,ServerConfig.class);
}
public MyClient(String host, int port){
MyClient client = ...
// create a connection to the server
return client;
}
#Data
#AllArgsConstructor
public static class ServerDetails{
private int port;
private String host;
}
#Configuration
public static class ServerConfig{
static String host;
static int port;
#Bean
public void serverDetails(){
return new ServerDetails(host, port);
}
}
}
It feels very clunky though. Is there a better way?
Related
I am working on an application with a WebSocket and want to save the clients id and session to a manager but have difficulties to understand how to do this correct when I also want to be able to reach this from another class with autowire.
public class Client {
private String id;
private Session session;
private MessageHandler handler;
Client(String id, Session session, MessageHandler handler) {
this.id = id;
this.session = session;
this.handler = handler;
}
}
public class ClientsManager {
private Set<Client> clientSet = new CopyOnWriteArraySet<>();
public Set<Client> getClients() {
return this.clientSet;
}
public void addClient(Client client) {
this.clientSet.add(client);
}
public void removeClient(Client client) {
clientSet.remove(client);
}
}
public class WebsocketServerEndpoint {
public static final ClientsManager manageClients = new ClientsManager();
#OnOpen
public void onOpen(Session session, #PathParam("connectId") String connectId) throws IOException, EncodeException {
MessageHandler messageHandler = new MessageHandler();
Client client = new Client(connectId, session, messageHandler);
this.client = client;
manageClients.addClient(client);
}
....
....
....
....
}
From another class:
public class DoSomething {
#Autowired
WebsocketServerEndpoint serverEndpoint;
public String doSomething() {
int numberOfClients = serverEndpoint.getClients().size()
return numberOfClients;
}
}
As I understand. This is not correct and you should not autowire static fields and so.
I can see when I debug that serverEndpoint: null in my DoSomething class but I get 1 connected client if I have one connected and so on.
When I do like this I will get the right number of clients in DoSomething class.
Have I just misunderstood this and it works as I have done?
or how should I do instead?
Is their a better way to write my Client and ClientsManager classes?
What I have read that if I would like to "Autowire" anyway there is two possible ways.
Using Constructor #Autowired For Static Field
Using #PostConstruct to set the value to Static Field
But how does this work when I would like to instantiate "public static final ClientsManager manageClients = new ClientsManager();"
Sorry for my stupid question but I feel I do not fully understand this.
If you would like to understand more about this topic search for Spring Dependency injection, but I write a short summary.
To be able to #Autowire a component you have to create a #Bean or #Service or #Component.
Creating beands first create a Configuration class, and a Beand or Beans inside.
#Configuration
public class Configuration {
#Value("${configuration.property.name}")
String username;
#Bean
public WebsocketServerEndpoint ebsocketServerEndpoint () {
return new WebsocketServerEndpoint();
}
}
#Value is not necessaty just good to mention with this annotation you can get a property name from spring application.properties file.
After this point you have created a #Bean instance of your class it is registered as a singleton class. You can get this one copy class from anywhere in your application you just have to, autowire it.
Or user construcor based dependency injection. ( #Autowired is not prefered).
Dont create beans just add #Component annotation to your class that you want to Autowire but I show a constructor injection.
#Component
public class WebsocketServerEndpoint {
public String test(){
return "test";
}
}
#RestController
public class DoSomething {
private final WebsocketServerEndpoint websocketHandler;
public DoSomething(WebsocketServerEndpoint websocketHandler) {
this.websocketHandler = websocketHandler;
}
#GetMapping(value = "/test")
public String test() {
return websocketHandler.test();
}
}
You can even test this endpoint with a curl GET request. curl http://localhost:8080/test
I have a configuration file called proxyconfig.java that reads from a yaml file and injects it into ProxyConfig.java file. I need to use the properties from my config to inject it into Httputil class to set up a proxy but i'm getting a null pointer exception as it doesn't inject the properties host and port to set up the proxy. I'm using the StaticContextAccessor to access the proxy config class and get the host and port values. Please could someone assist in what i'm currently doing wrong?
#Configuration
public class ProxyConfig {
#Value("${proxy.host}")
private String host;
#Value("${proxy.port}")
private int port;
public int getPort() { return this.port; }
public int getHost() { return this.host; }
}
StaticContextAccessor class:
public class StaticContextAccessor {
private static StaticContextAccessor instance;
#Autowired
private ApplicationContext applicationContext;
#PostConstruct()
public void registerInstance() {
instance = this;
}
public static <T> T getBean(Class<T> clazz) {
return instance.applicationContext.getBean(clazz);
}
}
My HttpUtil class:
public class Httputil {
private static RequestConfig defaultRequestConfig = RequestConfig.custom()
.setConnectTimeout(1000)
.setSocketTimeout(10000)
.setConnectionRequestTimeout(10000)
// setting the proxy
//causes null pointer as it cannot read host and port
.setProxy(new HttpHost(StaticContextAccessor.getBean(ProxyConfig.class).getHost(), StaticContextAccessor.getBean(ProxyConfig.class).getPort())
}
private static RequestConfig defaultRequestConfig = RequestConfig.custom()
.setConnectTimeout(1000)
.setSocketTimeout(10000)
.setConnectionRequestTimeout(10000)
// setting the proxy
//causes null pointer as it cannot read host and port
.setProxy(new HttpHost(StaticContextAccessor.getBean(ProxyConfig.class).getHost(), StaticContextAccessor.getBean(ProxyConfig.class).getPort())
In this code defaultRequestConfig is static so it is initialized when the class is loaded. At that point:
Spring is not intialized
If StaticContextAccessor is actually a bean, it's not initialized so instance is null.
You need to re-think this whole design. Why not have everything as Spring managed bean? Why not have defaultRequestConfig as bean? Why all these statics?
I have this requirement in which every client must have his data stored individually in a separated database.
I would like to achieve the following structure:
A global microservice handles authentication and also provide information about the database in which the client data is stored.
The others microservices, when requested, query the auth service to know the client database information, only then the entity manager gets produced.
I am struggling to properly manage the state of the EntityManagerFactory instances.
I've tried to store in in a WeakHashMap but some buggy things started to happen. Like a simple findById throwing exceptions.
I am actually using JEE with DeltaSpike data running on a Payara server.
Anyone have ever done that using a similar stack?
If you are using bean managed transaction, then it becomes even easier to use CDI to manage this kind of entity manager factory resource.
First create a datasource context annotation.
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({TYPE, PARAMETER, FIELD, METHOD})
public #interface Datasource {
/**
* This may be the database url or whatever.
*/
#Nonbinding
String value() default "";
}
#SuppressWarnings("AnnotationAsSuperInterface")
public class DatasourceLiteral extends AnnotationLiteral<Datasource> implements Datasource {
private static final long serialVersionUID = 7485753390480718735L;
private final String dbName;
public DatasourceLiteral(final String dbName) {
this.dbName = dbName;
}
#Override
public String value() {
return dbName;
}
}
#ApplicationScoped
public class EntityManagerFactoryProvider {
#Produces
#Datasource
#ApplicationScoped
public EntityManagerFactory entityManagerFactory(final InjectionPoint ip) {
final Annotated annotated = ip.getAnnotated();
final Datasource datasource = annotated.getAnnotation(Datasource.class);
/**
* Add relevant jpa properties
*/
final Map<String, String> jpaProperties = new HashMap<>();
/**
* The main point is here.
*/
jpaProperties.put("javax.persistence.jdbc.url", datasource.value());
return Persistence.createEntityManagerFactory("persistence-unit-jpa", jpaProperties);
}
public void dispose(#Disposes #Datasource final EntityManagerFactory emf) {
emf.close();
}
}
#ApplicationScoped
public class ExampleUserDatasource {
#Any
#Inject
private Instance<EntityManagerFactory> entityManagerFactories;
public void doSomething(final String user) {
final UserInfo userInfo = authenticationService.getUser(user);
final Datasource datasource = new DatasourceLiteral(userInfo.getDatasource());
final EntityManagerFactory entityManagerFactory = entityManagerFactories.select(datasource).get();
/**
* You could also actually inject this.
* Do whatever you want with it inside a transaction and close it too.
*/
final EntityManager entityManager = entityManagerFactory.createEntityManager();
}
}
I am unable to Inject Configuration in normal utility classes ,it works fine in controller class.
#Inject
Configuration configuration
public class EmailService {
public static boolean sendEmail(final AppUser appUser,final String mailString,String subject) {
final Properties props = new Properties();
final String auth = Play.application().configuration().getString("mail.smtp.auth")
final String starttls = Play.application().configuration().getString("mail.smtp.starttls.enable");
}
}
Actually I want to remove Depricated Play.application() and want to use Configuration for that I want to Inject Configuration in this class.
Dependency injection frameworks inject dependencies when constructing instances of objects, so if you want a method to use an injected component it has to be non-static.
You want to do something like this, making your sendEmail method non-static, and providing an interface:
#ImplementedBy(EmailServiceImpl.class)
public interface EmailService {
void sendEmail(AppUser appUser, String mailString, String subject);
}
#Singleton
public class EmailServiceImpl implements EmailService {
private final Configuration config;
#Inject
public EmailServiceImpl(Configuration config) {
this.config = config;
}
public void sendEmail(AppUser appUser, String mailString, String subject) {
final Properties props = new Properties();
final String auth = config.getString("mail.smtp.auth")
final String starttls = config.getString("mail.smtp.starttls.enable");
}
}
Then inject an EmailService instance into your controller constructors.
Note: this uses #ImplementedBy for simplicity but you could also configure the binding in your Guice module, allowing you to mock it in tests.
For the past 2 weeks things have been going great in my application. Last night I login remotely to work to find out that when I run my application my ApplicationContextProvider class no longer has knowledge of the Application Context. I've run Maven clean & build in addition to rebooting my PC. Can't seem to shake it...
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
public void setApplicationContext (ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
}
My Main class:
public static void main(String[] args) throws IOException {
System.setProperty("java.util.logging.SimpleFormatter.format", "%4$s: %5$s%n");
final HttpServer server = HttpServer.createSimpleServer(".", 80);
WebappContext ctx = new WebappContext("ProductionQueue", "/");
//enable annotation configuration
ctx.addContextInitParameter("contextClass", "org.springframework.web.context.support.AnnotationConfigWebApplicationContext");
ctx.addContextInitParameter("contextConfigLocation", "com.production");
//allow spring to do all of it's stuff
ctx.addListener("org.springframework.web.context.ContextLoaderListener");
....
ctx.deploy(server);
server.start();
//start the production process
Production.init();
System.in.read();
server.stop();
My Production class:
public class Production {
private static final Logger logger = Logger.getLogger(Production.class.getName());
/* A list of active workflows */
private static List<Workflow> workflowList = new ArrayList<Workflow>();
private static ProductionService productionService;
/**
* Initialize the production line
*/
public static void init() {
logger.info("Initializing production workflows...");
ApplicationContext context = ApplicationContextProvider.getApplicationContext(); //THIS IS NULL
productionService = (ProductionService) context.getBean("productionService");
No configuration has been modified at all. Within my config class I do have a bean for it...
#Configuration
#ComponentScan(basePackages = {
"com.production"
})
#PropertySource(value= {
"classpath:/application.properties",
"classpath:/environment-${FETTER_ENVIRONMENT}.properties"
})
#EnableJpaRepositories("com.production.repository")
#EnableTransactionManagement
public class Config {
#Value("${db.url}")
String PROPERTY_DATABASE_URL;
#Value("${db.user}")
String PROPERTY_DATABASE_USER;
#Value("${db.password}")
String PROPERTY_DATABASE_PASSWORD;
#Value("${persistenceUnit.default}")
String PROPERTY_DEFAULT_PERSISTENCE_UNIT;
#Value("${hibernate.dialect}")
String PROPERTY_HIBERNATE_DIALECT;
#Value("${hibernate.format_sql}")
String PROPERTY_HIBERNATE_FORMAT_SQL;
#Value("${hibernate.show_sql}")
String PROPERTY_HIBERNATE_SHOW_SQL;
#Value("${entitymanager.packages.to.scan}")
String PROPERTY_ENTITYMANAGER_PACKAGES_TO_SCAN;
#Bean
public ApplicationContextProvider applicationContextProvider() {
return new ApplicationContextProvider();
}
I'd say its mudsoup between the try to have it static and use it as a bean.
You creating a new instance of the ApplicationContextProvider as a spring bean. This is ApplicationContextAware and so gets the AC injected. But THEN you do not use said bean, you use its static getter to read the field, yet this, static thing never received the AC in the first place. You're never using your actual bean.
I'd say scratch that provider completly, and rely soley on the ApplicationContextAware interface, it does what you want, ie it was designed to do exactly that, why use a delegating bean?
I do not know if
#Bean
public ApplicationContextProvider applicationContextProvider() {
return new ApplicationContextProvider();
}
the ApplicationContextAware interface.
Try to add #Component at ApplicationContextProvider class and then remove the #Bean. I hope that the ApplicationContextAware` interface is taken in account if this class is found by your normal component scan.
Turns out there was a buried exception my logging was preventing me from getting access to. Thanks for help.