package io.budgetapp;
import com.bazaarvoice.dropwizard.assets.ConfiguredAssetsBundle;
import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module;
import io.budgetapp.application.ConstraintViolationExceptionMapper;
import io.budgetapp.application.DataConstraintExceptionMapper;
import io.budgetapp.application.NotFoundExceptionMapper;
import io.budgetapp.application.SQLConstraintViolationExceptionMapper;
import io.budgetapp.auth.DefaultAuthorizer;
import io.budgetapp.auth.DefaultUnauthorizedHandler;
import io.budgetapp.auth.TokenAuthenticator;
import io.budgetapp.configuration.AppConfiguration;
import io.budgetapp.crypto.PasswordEncoder;
import io.budgetapp.dao.*;
import io.budgetapp.job.RecurringJob;
import io.budgetapp.managed.JobsManaged;
import io.budgetapp.managed.MigrationManaged;
import io.budgetapp.model.*;
import io.budgetapp.resource.*;
import io.budgetapp.service.FinanceService;
import io.dropwizard.Application;
import io.dropwizard.auth.AuthDynamicFeature;
import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.auth.oauth.OAuthCredentialAuthFilter;
import io.dropwizard.configuration.EnvironmentVariableSubstitutor;
import io.dropwizard.configuration.SubstitutingSourceProvider;
import io.dropwizard.db.DataSourceFactory;
import io.dropwizard.hibernate.HibernateBundle;
import io.dropwizard.hibernate.UnitOfWorkAwareProxyFactory;
import io.dropwizard.migrations.MigrationsBundle;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature;
import org.tuckey.web.filters.urlrewrite.UrlRewriteFilter;
import javax.servlet.DispatcherType;
import javax.servlet.FilterRegistration;
import java.util.EnumSet;
/**
*
*/
public class BudgetApplication extends Application<AppConfiguration> {
public static void main(String[] args) throws Exception {
new BudgetApplication().run(args);
}
private final HibernateBundle<AppConfiguration> hibernate = new HibernateBundle<AppConfiguration>(User.class, Category.class, Budget.class, BudgetType.class, Transaction.class, Recurring.class, AuthToken.class) {
@Override
protected Hibernate5Module createHibernate5Module() {
Hibernate5Module module = super.createHibernate5Module();
// allow @Transient JPA annotation process by Jackson
module.disable(Hibernate5Module.Feature.FORCE_LAZY_LOADING);
module.disable(Hibernate5Module.Feature.USE_TRANSIENT_ANNOTATION);
return module;
}
@Override
public DataSourceFactory getDataSourceFactory(AppConfiguration configuration) {
return configuration.getDataSourceFactory();
}
};
@Override
public String getName() {
return "budget-app";
}
@Override
public void initialize(Bootstrap<AppConfiguration> bootstrap) {
MigrationsBundle<AppConfiguration> migrationBundle = new MigrationsBundle<AppConfiguration>() {
@Override
public DataSourceFactory getDataSourceFactory(AppConfiguration configuration) {
return configuration.getDataSourceFactory();
}
};
// allow using Environment variable in yaml
bootstrap.setConfigurationSourceProvider(
new SubstitutingSourceProvider(
bootstrap.getConfigurationSourceProvider(),
new EnvironmentVariableSubstitutor(false)
)
);
bootstrap.addBundle(migrationBundle);
bootstrap.addBundle(hibernate);
bootstrap.addBundle(new ConfiguredAssetsBundle("/app", "/app", "index.html"));
}
@Override
public void run(AppConfiguration configuration, Environment environment) {
// password encoder
final PasswordEncoder passwordEncoder = new PasswordEncoder();
// DAO
final CategoryDAO categoryDAO = new CategoryDAO(hibernate.getSessionFactory(), configuration);
final BudgetDAO budgetDAO = new BudgetDAO(hibernate.getSessionFactory(), configuration);
final BudgetTypeDAO budgetTypeDAO = new BudgetTypeDAO(hibernate.getSessionFactory());
final UserDAO userDAO = new UserDAO(hibernate.getSessionFactory());
final TransactionDAO transactionDAO = new TransactionDAO(hibernate.getSessionFactory());
final RecurringDAO recurringDAO = new RecurringDAO(hibernate.getSessionFactory());
final AuthTokenDAO authTokenDAO = new AuthTokenDAO(hibernate.getSessionFactory());
// service
final FinanceService financeService = new FinanceService(hibernate.getSessionFactory(), userDAO, budgetDAO, budgetTypeDAO, categoryDAO, transactionDAO, recurringDAO, authTokenDAO, passwordEncoder);
// jobs
final RecurringJob recurringJob = new UnitOfWorkAwareProxyFactory(hibernate).create(RecurringJob.class, FinanceService.class, financeService);
// resource
environment.jersey().register(new UserResource(financeService));
environment.jersey().register(new CategoryResource(financeService));
environment.jersey().register(new BudgetResource(financeService));
environment.jersey().register(new TransactionResource(financeService));
environment.jersey().register(new RecurringResource(financeService));
environment.jersey().register(new ReportResource(financeService));
// health check
environment.jersey().register(new HealthCheckResource(environment.healthChecks()));
// managed
environment.lifecycle().manage(new MigrationManaged(configuration));
environment.lifecycle().manage(new JobsManaged(recurringJob));
// auth
TokenAuthenticator tokenAuthenticator = new UnitOfWorkAwareProxyFactory(hibernate).create(TokenAuthenticator.class, FinanceService.class, financeService);
final OAuthCredentialAuthFilter<User> authFilter =
new OAuthCredentialAuthFilter.Builder<User>()
.setAuthenticator(tokenAuthenticator)
.setPrefix("Bearer")
.setAuthorizer(new DefaultAuthorizer())
.setUnauthorizedHandler(new DefaultUnauthorizedHandler())
.buildAuthFilter();
environment.jersey().register(RolesAllowedDynamicFeature.class);
environment.jersey().register(new AuthDynamicFeature(authFilter));
environment.jersey().register(new AuthValueFactoryProvider.Binder(User.class));
// filters
FilterRegistration.Dynamic urlRewriteFilter = environment.servlets().addFilter("rewriteFilter", UrlRewriteFilter.class);
urlRewriteFilter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD), false, "/*");
urlRewriteFilter.setInitParameter("confPath", "urlrewrite.xml");
// only enable for dev
// FilterRegistration.Dynamic filterSlow = environment.servlets().addFilter("slowFilter", SlowNetworkFilter.class);
// filterSlow.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD), false, "/*");
// exception mapper
environment.jersey().register(new NotFoundExceptionMapper());
environment.jersey().register(new DataConstraintExceptionMapper());
environment.jersey().register(new ConstraintViolationExceptionMapper());
environment.jersey().register(new SQLConstraintViolationExceptionMapper());
}
}