Is it the right way to implements spring InitializingBean like this? - java

I want to assign value for host and port field in class IdGenerator,which is the best way to achieve this?
Note:IdGenerator is best not be managed by spring,
the client class can call genId() as a static method.
#Component
public class IdGenerator implements InitializingBean{
private static final Logger LOGGER = LoggerFactory.getLogger(IdGenerator.class);
#Value("${vesta.host}")
private String host;
#Value("${vesta.port}")
private Integer port;
static VestaHttpClient client;
#Override
public void afterPropertiesSet() {
Assert.hasText(host);
Assert.notNull(port);
LOGGER.info("about to initial IdGenerator.");
try {
client = new VestaHttpClient(host, port);
}catch(Exception e){
LOGGER.info("IdGenerator initialize failed .");
throw new RuntimeException("----------VestaHttpClient initialize failed--------");
}
LOGGER.info("IdGenerator was successfully initialized.");
}
public static String genId(){
return client.genId()+"";
}
}

You could simply build VestaHttpClient as a bean in a #Configuration class, using a PropertyPlaceholderConfigurer to grab the property values and inject them into fields annotated with #Value. Here is some code I mocked to describe my recommendation (it may require some adjustments):
#Configuration
#PropertySource("classpath:example.properties")
public class MyConfiguration {
#Value("${vesta.host}")
private String host;
#Value("${vesta.port}")
private Integer port;
#Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
#Bean
public VestaHttpClient get httpClient(){
VestaHttpClient httpClient = null;
try {
client = new VestaHttpClient(host, port);
}catch(Exception e){
/*Omitted*/
}
return httpClient;
}
}

If you want to inject the property values using field injection, so called “clean” configuration bean, then InitializingBean is the right way to validate your properties.
However another way is to use just constructor injection and get rid of InitializingBean
#Component
public class IdGenerator {
private static final Logger LOGGER = LoggerFactory.getLogger(IdGenerator.class);
private String host;
private Integer port;
static VestaHttpClient client;
#Autowired
public WebProperties(#Value("${vesta.host}") String protocol,
#Value("${vesta.port}") Integer port) {
// Validate properties and initialize VestaHttpClient
}
}

Related

In Spring Boot which class would be initialize first #Component or #Configuration

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
}

Spring Bean - Constructor with arguments

I see an error in the constructor which says
"String queueName" cannot be autowired.
I have AmazonSQSAsync component defined in another class but not
queueName. Why is the constructor trying to autowire the parameters
and how can I resolve this?
#Configuration
public class SqsQueueHealthIndicator extends AbstractHealthIndicator {
private final AmazonSQSAsync amazonSQSAsync;
private final String queueName;
public SqsQueueHealthIndicator(AmazonSQSAsync amazonSQSAsync, String queueName) {
this.amazonSQSAsync = amazonSQSAsync;
this.queueName = queueName;
}
#Override
protected void doHealthCheck(Health.Builder builder) {
try {
amazonSQSAsync.getQueueUrl(queueName);
builder.up();
} catch (QueueDoesNotExistException e) {
builder.down(e);
}
}
#Bean
SqsQueueHealthIndicator queueHealthIndicator(#Autowired AmazonSQSAsync amazonSQSAsync, #Value("${url}") String queueName) {
return new SqsQueueHealthIndicator(amazonSQSAsync, queueName);
}
#Bean
SqsQueueHealthIndicator deadLetterQueueHealthIndicator(#Autowired AmazonSQSAsync amazonSQSAsync, #Value("${dlqurl}") String deadLetterQueueName) {
return new SqsQueueHealthIndicator(amazonSQSAsync, deadLetterQueueName);
}
}
Because you're declaring it as final so its value must be initialized.
You did not provide default value so Spring understand its value will be injected.
So you have 2 options:
Remove the final modifier. Use #Value to inject your value from config file.
Create a bean type of String and inject it. You should name it:
#Bean("queueName")
public String getQueueName {return "xyz";}
And inject like:
#Autowire
#Qualifier("queueName")
private final String queueName;
In normal circumstance, the option 1 is the go-to.

How to use the config string and DAO in a resource in Dropwizard

I want to use a String from my config.yml and inject some DAO with guice in a resource. Consider the following code example
#Path("/upload")
#Produces(MediaType.APPLICATION_JSON)
public class UploadResource {
private static String UPLOAD_PATH;
public UploadResource(String uploadPath) {
this.UPLOAD_PATH = uploadPath;
}
}
I have added a the config.yml parameter in my constructor and used the following command to add the string in the application class
final UploadResource uploadResource = new UploadResource(
configuration.getUploadFileLocation());
environment.jersey().register(uploadResource);
Usually I would inject some Dao as follows
#Produces(MediaType.APPLICATION_JSON)
public class UploadResource {
private final SomeDao someDao;
#Inject
public UploadResource(SomeDao someDao) {
this.someDao = someDao;
}
}
but since my constructor has already an string entry. how would I handle this elegantly with Dropwizard? simple extending the arguments seems to be unclean.
So if you are using Dropwizard with Guice and would like to use some string in from the config.yml you need to do the following
to the resource you
#Path("/upload")
#Produces(MediaType.APPLICATION_JSON)
public class UploadResource {
private final FileDao fileDao;
private final String uploadPath;
#Inject
public UploadResource(FileDao fileDao, #Named("{someName}") String uploadPath) {
this.fileDao = fileDao;
this.uploadPath = uploadPath;
}
}
and in the application class you need to bind a constant as follows
#Override
public void run(
final BackendConfiguration configuration,
final Environment environment) {
Injector injector =
Guice.createInjector(
new AbstractModule() {
#Override
protected void configure() {
bindConstant().annotatedWith(Names.named("{someName}"))
.to(configuration.getUploadFileLocation());
}
});
Make sure you implemented the method getUploadFileLocation as discribed by lutz from this post here

Use Dropwizard configuration in a method that establishes a connection to a MongoDB database

I am coding Dropwizard micro-services that fetch data in a MongoDB database. The micro-services work fine but I'm struggling to use in my DAO the configuration coming from my Dropwizard configuration Java class. Currently I have
public class XDAO implements IXDAO {
protected DB db;
protected DBCollection collection;
/* singleton */
private static XDAO instance;
/* Get singleton */
public static synchronized XDAO getSingleton(){
if (instance == null){
instance = new XDAO();
}
return instance;
}
/* constructor */
public XDAO(){
initDatabase();
initDatabaseIndexes();
}
private void initDatabase(){
MongoClient client = null;
try {
client = new Mongo("10.126.80.192",27017);
db = client.getDB("terre");
//then some other code
}
catch (final MongoException e){
...
}
catch (UnknownHostException e){
...
}
}
}
I want to unhard-code the three arguments in these two lines :
client = new Mongo("10.126.80.192", 27017);
db = client.getDB("terre");
My MongoConfiguration Java class is :
public class MongoConfiguration extends Configuration {
#JsonProperty
#NotEmpty
public String host;
#JsonProperty
public int port = 27017;
#JsonProperty
#NotEmpty
public String db_name;
public String getMongohost() {
return host;
}
public void setMongohost(String host) {
this.host = host;
}
public int getMongoport() {
return port;
}
public void setMongoport(int port) {
this.port = port;
}
public String getDb_name() {
return db_name;
}
public void setDb_name(String db_name) {
this.db_name = db_name;
}
}
My Resource class that uses the DAO is :
#Path("/mongo")
#Produces(MediaType.APPLICATION_JSON)
public class MyResource {
private XDAO xDAO = XDAO.getSingleton();
private String mongohost;
private String db_name;
private int mongoport;
public MyResource(String db_name, String mongohost, int mongoport) {
this.db_name = db_name;
this.mongohost = mongohost;
this.mongoport = mongoport;
}
public MyResource() {
}
#GET
#Path("/findByUUID")
#Produces(value = MediaType.APPLICATION_JSON)
#Timed
public Entity findByUUID(#QueryParam("uuid") String uuid) {
return xDAO.findByUUid(uuid);
}
}
And in my application class there is
#Override
public void run(final MongoConfiguration configuration, final Environment environment) {
final MyResource resource = new MyResource(configuration.getDb_name(), configuration.getMongohost(), configuration.getMongoport());
environment.jersey().register(resource);
}
To solve my problem I tried many things. The last thing I tried was to add these four fields in my XDAO
private String mongohost;
private String db_name;
private int mongoport;
private static final MongoConfiguration configuration = new MongoConfiguration();
Coming with this piece of code in the constructor of the XDAO:
public XDAO(){
instance.mongohost = configuration.getMongohost();
instance.mongoport = configuration.getMongoport();
instance.db_name = configuration.getDb_name();
/* then like before */
initDatabase();
initDatabaseIndexes();
}
When I try this I have a null pointer exception when my initDatabase method is invoked : mongoHost and db_name are null
The problem is that you are creating a new configuration in your XDAO with private static final MongoConfiguration configuration = new MongoConfiguration(); instead of using the config from Dropwizard's run method.
When you do this, the fields host and db_name in the new configuration are null, which is why you are getting the NPE when instantiating XDAO
You need to pass the instance of MongoConfiguration that you get from Dropwizard in your application class to your XDAO, ideally when the singleton XDAO is created so it has non-null values for db_name and host
This code below part of the problem - you are creating the singleton without giving XDAO the MongoConfiguration configuration instance.
public class XDAO implements IXDAO {
//... snip
/* Get singleton */
public static synchronized XDAO getSingleton(){
if (instance == null){
instance = new XDAO(); // no configuration information is included!
}
return instance;
}
/* constructor */
public XDAO(){
initDatabase(); // this call needs db_name & host but you haven't set those yet!!
initDatabaseIndexes();
}
I recommend you modify your application class to create XDAO along the lines of this:
#Override
public void run(final MongoConfiguration configuration, final Environment environment) {
XDAO XDAOsingleton = new XDAO(configuration);
XDAO.setSingletonInstance(XDAOsingleton); // You need to create this static method.
final MyResource resource = new MyResource(configuration.getDb_name(), configuration.getMongohost(), configuration.getMongoport()); // MyResource depends on XDAO so must be created after XAO's singleton is set
environment.jersey().register(resource);
}
You may also need to take initDatabase() etc out of XDAO's constructor depending on if you keep public static synchronized XDAO getSingleton()
I also recommend you change the constructor of MyResource to public MyResource(XDAO xdao). The resource class doesn't appear to need the configuration information, and it is better to make the dependency on an XDAO explicit (you then also don't need to keep the XDAO singleton in a static field inside XDAO's class).
To get MongoDB integrated in a simple way to Dropwizard, please try and use MongoDB Managed Object. I will explain this in 3 simple steps:
Step 1: Create a simple MongoManged class:
import com.mongodb.Mongo;
import io.dropwizard.lifecycle.Managed;
public class MongoManaged implements Managed {
private Mongo mongo;
public MongoManaged(Mongo mongo) {
this.mongo = mongo;
}
#Override
public void start() throws Exception {
}
#Override
public void stop() throws Exception {
mongo.close();
}
}
Step 2: Mention MongoDB Host, Port, DB Name in a config yml file:
mongoHost : localhost
mongoPort : 27017
mongoDB : softwaredevelopercentral
Step 3: Bind everything together in the Application Class:
public class DropwizardMongoDBApplication extends Application<DropwizardMongoDBConfiguration> {
private static final Logger logger = LoggerFactory.getLogger(DropwizardMongoDBApplication.class);
public static void main(String[] args) throws Exception {
new DropwizardMongoDBApplication().run("server", args[0]);
}
#Override
public void initialize(Bootstrap<DropwizardMongoDBConfiguration> b) {
}
#Override
public void run(DropwizardMongoDBConfiguration config, Environment env)
throws Exception {
MongoClient mongoClient = new MongoClient(config.getMongoHost(), config.getMongoPort());
MongoManaged mongoManaged = new MongoManaged(mongoClient);
env.lifecycle().manage(mongoManaged);
MongoDatabase db = mongoClient.getDatabase(config.getMongoDB());
MongoCollection<Document> collection = db.getCollection(config.getCollectionName());
logger.info("Registering RESTful API resources");
env.jersey().register(new PingResource());
env.jersey().register(new EmployeeResource(collection, new MongoService()));
env.healthChecks().register("DropwizardMongoDBHealthCheck",
new DropwizardMongoDBHealthCheckResource(mongoClient));
}
}
I have used these steps and written a blog post and a sample working application code is available on GitHub. Please check: http://softwaredevelopercentral.blogspot.com/2017/09/dropwizard-mongodb-tutorial.html

How can I a get singleton from Guice that's configured with runtime parameters?

My overall goal is to load properties from a properties file and then inject those properties into my objects. I would also like to use those properties to instantiate certain singleton classes using Guice. My singleton class looks like this:
public class MainStore(){
private String hostname;
#Inject
public void setHostname(#Named("hostname") String hostname){
this.hostname = hostname;
}
public MainStore(){
System.out.println(hostname);
}
}
I'm trying to instantiate a singleton using this provider:
public class MainStoreProvider implements Provider<MainStore> {
#Override
public MainStore get(){
MainStore mainStore = new MainStore();
return mainStore;
}
}
My configurationModule is a module that loads a configuration from a property file specified at runtime:
public class ConfigurationModule extends AbstractModule {
#Override
protected void configure(){
Properties properties = loadProperties();
Names.bindProperties(binder(), properties);
}
private static Properties loadProperties() {
String resourceFileName = "example.properties";
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
InputStream inputStream = classLoader.getResourceAsStream(resourceFileName);
Properties properties = new Properties();
properties.load(inputStream);
return properties;
}
}
And my example.properties files contains:
hostname = testHostName
Then when I need the MainStore singleton I'm using:
Injector injector = Guice.createInjector(new ConfigurationModule());
MainStoreProvider mainStoreProvider = injector.getInstance(MainStoreProvider.class);
MainStore mainStore = mainStoreProvider.get(); //MainClass singleton
Is this the right path to go down? Should I be doing this a completely different way? Why does my MainStore not print out the correct hostname?
I have written up a small example that demonstrates how to bind a Singleton, how to inject a property and so on.
public class TestModule extends AbstractModule {
#Override
protected void configure() {
Properties p = new Properties();
p.setProperty("my.test.string", "Some String"); // works with boolean, int, double ....
Names.bindProperties(binder(),p);
bind(X.class).to(Test.class).in(Singleton.class); // This is now a guice managed singleton
}
public interface X {
}
public static class Test implements X {
private String test;
#Inject
public Test(#Named("my.test.string") String test) {
this.test = test;
System.out.println(this.test);
}
public String getTest() {
return test;
}
}
public static void main(String[] args) {
Injector createInjector = Guice.createInjector(new TestModule());
Test instance = createInjector.getInstance(Test.class);
}
}
The configure method is now responsible to tell guice that my Test class is a singleton.
It prints the correct hostname (property test) because I inject the constructor and set the property.
You can do this with a provider as well, however you will then have to create your objects manually. In my case, this would look like this:
public static class TestProvider implements Provider<X> {
private String test;
private X instance;
public TestProvider(#Named("my.test.string") String test) {
this.test = test;
}
#Override
public X get() {
if(instance == null) {
instance = new Test(test);
}
return instance;
}
}
Binding will then look like this:
bind(X.class).toProvider(TestProvider.class);
Is this what you wanted?
Cheers,
Artur
Edit:
I did some test and found this to note:
You can bind a provider as a singleton:
bind(X.class).toProvider(TestProvider.class).in(Singleton.class);
That way you do not need to handle singleton creation yourself:
public static class TestProvider implements Provider<X> {
private String test;
private X instance;
#Inject
public TestProvider(#Named("my.test.string") String test) {
this.test = test;
}
#Override
public X get() {
return instance;
}
}
The above code will create singletons of the object X.

Categories