I am new to dropwizard, and am using GuiceBundle and MongoBundle in my application.
The MongoClient is wrapped in a dropwizard Managed object and is tied to the lifecycle of the application.
public class SalApplication extends Application<SomeConf> {
...
private GuiceBundle<SomeConf> guiceBundle;
private MongoBundle<SomeConf> mongoBundle;
...
#Override
public void initialize(Bootstrap<SomeConf> bootstrap) {
// build bundles and add to bootstrap
...
}
#Override
public void run(SomeConf someConf, Environment env) throws Exception{
...
MongoClient client = mongoBundle.getClient();
MongoClientManager mongoDB = new MongoClientManager(client);
env.lifecycle().manage(mongoDB); //MongoClientManager implements Managed
}
My hiccup is, how do I get hold of the MongoClient object.
The object is supposed to be injected into my DAOs.
But how can I access the MongoClient object from inside guice Module.
If I construct another MongoClient object inside guice module, then what is the point of the Managed Object. I'm really confused.
I would recommend writing your own Guice module. While the dropwizard-guice is quite useful it has not been updated since Feb 2017 and only supports up to version 1.0.0 of DW. A basic version of your module could look like this:
public class CustomModule implements Module {
private final MongoClient mongoClient;
public CustomModule(MongoClient mongoClient) {
this.mongoClient = mongoClient;
}
#Override
public void configure(Binder binder) {
binder.bind(MongoClient.class).toInstance(mongoClient);
}
}
Which then you can initialize from the "run" method in your Application class using the Guice standard methods:
public class SalApplication extends Application<SomeConf> {
...
#Override
public void run(SomeConf someConf, Environment env) throws Exception{
Guice.createInjector(new CustomModule(mongoBundle.getClient());
...
}
}
I found a simpler way to achieve what I needed.
I was previously using com.meltmedia.dropwizard.dropwizard-mongo, whose MongoBundle constructs the MongoClient, which had to be passed to my ManagedObject.
I stopped using it. Instead I'm constructing the MongoClient object myself using mongo-java-driver inside guice module, and is injected into the constructor of my managed object.
Related
I have a bean with a constructor as follows. The password argument is resolved from the placeholder my.password, with a default value of DEFAULT. If the value of DEFAULT is passed, a warning is logged. Note - this Bean is contained within an imported third-party library.
#Bean
public class EncryptionBean {
public EncryptionBean(#Value("${my.password}") String password) {
if "DEFAULT".equals(password) {
// log warning message
} else {
// do stuff with the password
}
}
}
The password is retrieved at startup from an external system using a client SDK. This SDK object is itself provided as a Bean (also from a third-party library). After retrieving the password, I am setting it as a System property for the above EncryptionBean to have access to at the time of instantiation:
#Configuration
public class MyConfiguration {
#Autowired
public SDKObject sdkObject;
#PostConstruct
public void init() {
System.setProperty("my.password", sdkObject.retrievePassword());
// #Value("${my.password"}) should now be resolvable when EncryptionBean is instantiated
}
}
However, EncryptionBean is still being instantiated with a value of DEFAULT for my.password. I'm wondering if System.setProperty in #PostConstruct might be getting executed AFTER Spring has already instantiated the instance of EncryptionBean?
If so, is there a way to guarantee this property has been set before Spring instantiates EncryptionBean? I came across #DependsOn as a way to control the order Beans get instantiated by Spring, but since EncryptionBean comes from a third-party library, I haven't been able to make this annotation work.
Instead of setting a system property, you should create a Spring EnvironmentPostProcessor class to retrieve the password from the external source and add it to the Spring Environment. That would look something like this:
public class PasswordEnvironmentPostProcessor implements EnvironmentPostProcessor, ApplicationContextAware {
private ApplicationContext applicationContext;
#Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
SDKObject sdkObject = applicationContext.getBean(SDKObject.class);
Map<String, Object> properties = Collections.singletonMap("my.password", sdkObject.retrievePassword());
MapPropertySource propertySource = new MapPropertySource("password", properties);
environment.getPropertySources().addFirst(propertySource);
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
Then you'll need to register this class with Spring by adding an entry to the file META-INF/spring.factories that looks like this:
org.springframework.boot.env.EnvironmentPostProcessor=com.example.PaswordEnvironmentPostProcessor
Documentation for this is available here: https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto.application.customize-the-environment-or-application-context.
I could not figure out a nice clean way to inject a property at runtime without a lot of boilerplate. But I came up with a clean way to do what you want to do without having to refresh the application context or messing with the 3rd party library implementation.
First we exclude the 3rd party bean from our application context:
#ComponentScan(excludeFilters = #ComponentScan.Filter(value = EncryptionBean.class, type = FilterType.ASSIGNABLE_TYPE))
#SpringBootApplication
public class SandboxApplication {
public static void main(String[] args) {
SpringApplication.run(SandboxApplication.class, args);
}
}
Then we create the Bean ourselves with the values we want.
#Configuration
public class MyConfiguration {
public final SDKObject sdkObject;
public MyConfiguration(SDKObject sdkObject) {
this.sdkObject = sdkObject;
}
#Bean
public EncryptionBean encryptionBean() {
return new EncryptionBean(sdkObject.retrievePassword());
}
}
I'm writing a web application with a framework that calls the run() method of my Application class and passes in an object of type Environment.
Other objects I'm writing depend on this Environment class, as they need to call its register() and metrics() methods.
The problem is that I create the object graph in the main() function of my application, like this:
public class MainApplication extends Application<MainConfiguration> {
private final ConnectionPool sharedPool;
public static void main(String[] args) throws Exception {
MainApplication mainApplication = DaggerMainApplicationComponent.create()
.createApplication();
mainApplication.run(args);
}
#Inject public MainApplication(ConnectionPool connectionPool) {
super();
sharedPool = connectionPool;
}
#Override
public void run(MainConfiguration configuration, Environment environment) throws Exception {
// Here is where I have access to the environment variable
}
So, by the time MainApplication is constructed by Dagger, the environment variable is not ready. It is only when run() is called that it is available.
Is there a way to inject this variable into the object graph at that point?
This kind of question has already had some traction here but to answer your specific case and to elaborate on EpicPandaForce's comment you can easily escape a small dependency cycle by creating a holder class:
class EnvironmentHolder {
private Environment environment;
#Nullable
Environment get() {
return environment;
}
void set(Environment environment) {
this.environment = environment;
}
}
And making the former dependencies of Environment dependencies of EnvironmentHolder instead:
class DependsOnEnvironment {
private final EnvironmentHolder environmentHolder;
#Inject
DependsOnEnvironment(EnvironmentHolder environmentHolder) {
this.environmentHolder = environmentHolder;
}
public void doSomethingWithMetrics() {
Environment environment = environmentHolder.get();
if (environment == null) {
throw new IllegalStateException("Environment object should be available at the injection time of this class");
}
Metrics metrics = environment.metrics();
//etc.
}
}
If you find yourself using this often it may be a sign that you need a custom scope instead.
I am developing a project which involves JAX-RS (for REST API) and Websocket (for notifications). The project will be deployed as a WAR into a application server.
For JAX-RS, I do the following:
#ApplicationPath("/")
public class MyApplicationREST extends ResourceConfig {
public MyApplicationREST() {
... initialization here ...
}
}
For Websockets, I do the following:
public class MyApplicationWebsockets implements ServerApplicationConfig {
... callbacks for discovery of endpoints here ...
}
Both classes are perfectly picked up by the application server (Tomcat in my case) when the WAR is deployed and work fine in vacuum.
However, in both classes, I need a reference to a command instance (being the database connection in this case, but it can be anything). I cannot instantiate it in one of the two classes above (and use it in the other), as there is no guarantee of the initialization order of the two classes.
What is the best way to do this?
Initialization
(1) Create a class that implements ServletContextListener.
(2) Write your initialization code in contextInitialized(ServletContextEvent) method.
public class MyContextListener implements ServletContextListener
{
#Override
public void contextInitialized(ServletContextEvent context)
{
// Your initialization code here.
}
#Override
public void contextDestroyed(ServletContextEvent context)
{
// Your finalization code here.
}
}
(3) Register the class as a listener in web.xml.
<listener>
<listener-class>com.example.MyContextListener</listener-class>
</listener>
Shared Instance
Regarding a shared instance, singleton pattern is one of possible means to achieve it.
public class DB
{
private static final DB sInstance = new DB();
// Private constructor to prevent DB instances from being created by others.
private DB()
{
}
// Get the singleton instance.
public static DB getInstance()
{
return sInstance;
}
}
I'm starting to work with Dropwizard and I'm trying to create a Command that requires to use the database. If someone is wondering why I'd want to do that, I can provide good reasons, but this is not the point of my question anyway. It's about dependency inversion and Service initialization and run phases in Dropwizard.
Dropwizard encourages to use its DbiFactory to build DBI instances but in order to get one, you need an Environment instance and/or the database configuration:
public class ConsoleService extends Service<ConsoleConfiguration> {
public static void main(String... args) throws Exception {
new ConsoleService().run(args);
}
#Override
public void initialize(Bootstrap<ConsoleConfiguration> bootstrap) {
bootstrap.setName("console");
bootstrap.addCommand(new InsertSomeDataCommand(/** Some deps should be here **/));
}
#Override
public void run(ConsoleConfiguration config, Environment environment) throws ClassNotFoundException {
final DBIFactory factory = new DBIFactory();
final DBI jdbi = factory.build(environment, config.getDatabaseConfiguration(), "postgresql");
// This is the dependency I'd want to inject up there
final SomeDAO dao = jdbi.onDemand(SomeDAO.class);
}
}
As you can see, you have the configuration for your Service and its Environment in its run() method, but commands need to be added to the Service's bootstrap in its initialize() method.
So far, I've been able to achieve this by extending ConfiguredCommand in my Commands and creating DBI instances inside their run() methods, but this is a bad design, because dependencies should be injected into the object instead of creating them inside.
I'd prefer to inject DAOs or any other dependencies of my Commands through their constructor, but this seems currently impossible to me, as the Environment and the configuration are not accesible in Service initialization, when I need to create and add them to its bootstrap.
Does anyone know how to achieve this?
Can you use the EnvironmentCommand?
This is how I use Guice with Dropwizard. Inside your run() method add the line
Guice.createInjector(new ConsoleModule());
Create the class ConsoleModule
public class ConsoleModule extends AbstractModule {
public ConsoleModule(ConsoleConfiguration consoleConfig)
{
this.consoleConfig = consoleConfig;
}
protected void configure()
{
bind(SomeDAO.class).to(SomeDAOImpl.class).in(Singleton.class)
}
}
I am using spring mvc+hibernate+two databases
So for example:
I create 2 sessionFactories. sessionFactory1 (using datasource1) and sessionFactory2 (using datasource2).
Would it be possible to change sessionFactory1 or sessionFactory2 to sessionFactory at runtime so that the dao/s references them. sessionFactory is already autowired to all dao/s.
I am searching for it right now I think #Configuration can help me but I am not sure.
I am trying AbstractRoutingDataSource but don't think it helps.
Usually Spring wires your beans on application startup, so "re-wiring" (replacing references to sessionFactory1 with references to sessionFactory2 on runtime) does not seem easy to implement.
Maybe you could implement a "proxy bean" that is wired to your DAO objects and change the "target SessionFactory" of your proxy bean.
AbstractRoutingDataSource will work for you.
First you'll need to create a class that will store the current DB in use:
public class MyContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
public static void setDBContext(String dBContext) {
contextHolder.set(dBContext);
}
public static String getDBContext() {
return (String) contextHolder.get();
}
public static void clearDBContext() {
contextHolder.remove();
}
}
You'll need to create a class that extends this one and implements determineCurrentLookupKey(), and return the current db you have in your context holder:
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class MyRoutingDataSource extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
return MyContextHolder.getDBContext();
}
}
See the example in http://blog.springsource.org/2007/01/23/dynamic-datasource-routing/.
It worked fine for me.