====== New Java Web Applications ======
End of life January 2019.
If you are making a company project :
* You **NEED** nexus access, contact anthony.arents@mentoringsystems.be
All new projects have specific requirements in the buildprocess :
* Netbeans project
* Maven
* Spring (latest -> 4.3.12.RELEASE)
* Spring Security (latest -> 4.2.3.RELEASE)
* Hibernate (latest -> 5.2.12.Final)
* Javamelody (latest, developer tool, used for statistics)
* ExtJS 6
* Tomcat 7 or 8 (DOWNLOAD NEWEST FROM THEIR WEBSITE! required for java config, **earlier versions supplied with netbeans are bad**)
* Use Java 7 for compiling, I recommend Java 8 for running tomcat (no permgen errors) ;-).
Code examples were taken from Merke or BoardPortal. **This documentation is still a work in progress.**
====== Tracking versions ======
A website which allows you to keep a list of dependencies to watch :\\
https://www.artifact-listener.org/\\
\\
My list (on the right of the screenshot)\\
{{:java:screenshot-www.artifact-listener.org_2017-07-06_11-41-02.png?nolink&600|}}
\\
Spring Blog
https://spring.io/blog\\
====== Thread safety is important ======
Be aware, sometimes, a method is not safe for use in webapplications, prevent setting things static at all time\\
unless they are threadsafe (like Log4J).\\
For example : SimpleDateFormat, when made static is thread unsafe & may start confusing dates passed to it.
====== Maven site usage ======
Please use the maven site ! It will calculate PMD + Findbugs for you.\\
Just rightclick the project : "Run Maven" -> "Goals ..." -> type "site" & save as "site".\\
You'll be able to run it again with "Run Maven" -> "site".\\
\\
In the generated output, there's a link : "View generated site". You can use this link when Maven is finished.\\
\\
You **should** solve all Findbugs & PMD errors.
====== Package structure : ======
''be.mentoringsystems.applicationname.config''
Contains Configuration classes used to configure the application
''be.mentoringsystems.applicationname.config.initializer''
Contains 2 classes: one to create the servlet and one to initialize spring security
''be.mentoringsystems.applicationname.dao''
Contains DAO interfaces, naming ObjectnameDAO
''be.mentoringsystems.applicationname.dao.hibernate''
Contains DAO implementations, naming ObjectnameDAOImpl, extends HibernateDaoSupport, implements ObjectnameDAO
''be.mentoringsystems.applicationname.model''
Contains helper models which may be used throughout the application (for example QueryParams)
''be.mentoringsystems.applicationname.model.db''
Contains models which will be stored in the database, naming Ojectname
''be.mentoringsystems.applicationname.model.dto''
Contains models which will be intermediaries for the Controller interfaces (forms sent to server end up in a dto which are then translated to models in the service api)
''be.mentoringsystems.applicationname.presentation''
Contains viewcontrollers, Restcontrollers have their own package to make it easier to find them.
''be.mentoringsystems.applicationname.presentation.conversion''
Contains convertors for deserializing (spring).
''be.mentoringsystems.applicationname.presentation.excel''
Contains excel views using apache poi
''be.mentoringsystems.applicationname.presentation.rest''
Contains controllers which will be accessed via Rest
''be.mentoringsystems.applicationname.presentation.serializer''
Contains Jackson serializers
''be.mentoringsystems.applicationname.service''
Contains Service interfaces, naming ObjectnameService
''be.mentoringsystems.applicationname.service.impl''
Contains Service implementations, naming ObjectnameServiceImpl, implements ObjectnameService, uses ObjectnameDAO & Transactional annotations
===== German Projects =====
''de.jcpis.applicationname''
see above for details.
====== How to start : ======
Start with creating mocks & database model
Create a new project : ''New Project -> Maven -> Web Application''
===== POM : =====
Change the POM (configuration files -> ''pom.xml'') to this :
4.0.0be.mentoringsystemsMerke1.0-SNAPSHOTwarMerkejava8-doclint-disabled[1.8,)-Xdoclint:none3.0.12.2.14.3.10.RELEASE4.2.3.RELEASE5.1.442.1.15.2.10.Final2.8.101.2.52.9.03.62.51.9.31.6.03.161.69.03.0.21.71.7UTF-82.92.52.10.42.173.72.203.0.52.42.33.63.6.22.8.23.0.02.5.23.0.03.1.03.0.22.202.2javax.servletjavax.servlet-api${javax.servletapi}providedjavax.servlet.jspjavax.servlet.jsp-api${javax.servletjspapi}providedorg.apache.taglibstaglibs-standard-spec${apache.taglibs.version}org.apache.taglibstaglibs-standard-impl${apache.taglibs.version}org.springframeworkspring-core${spring.version}org.springframeworkspring-web${spring.version}org.springframeworkspring-webmvc${spring.version}org.springframeworkspring-orm${spring.version}org.springframeworkspring-context-support${spring.version}org.springframework.securityspring-security-web${springsecurity.version}org.springframework.securityspring-security-config${springsecurity.version}org.springframework.securityspring-security-taglibs${springsecurity.version}org.apache.commonscommons-dbcp2${dbcp.version}org.apache.commonscommons-lang3${commons.lang.version}mysqlmysql-connector-java${mysql.version}org.hibernatehibernate-core${hibernate.version}com.fasterxml.jackson.corejackson-core${jackson.version}com.fasterxml.jackson.corejackson-databind${jackson.version}com.fasterxml.jackson.corejackson-annotationscom.fasterxml.jackson.corejackson-annotations${jackson.version}com.fasterxml.jackson.dataformatjackson-dataformat-xml${jackson.version}com.fasterxml.jackson.corejackson-annotationscom.fasterxml.jackson.dataformatjackson-dataformat-csv${jackson.version}com.fasterxml.jackson.corejackson-annotationscom.fasterxml.jackson.datatypejackson-datatype-hibernate5${jackson.version}com.fasterxml.jackson.corejackson-annotationsorg.apache.logging.log4jlog4j-core${log4j.version}org.apache.logging.log4jlog4j-api${log4j.version}org.apache.logging.log4jlog4j-web${log4j.version}org.apache.logging.log4jlog4j-slf4j-impl${log4j.version}commons-iocommons-io${commons.io.version}commons-beanutilscommons-beanutils${commons.beanutils.version}com.sun.mailjavax.mail${javax.mail.version}org.apache.poipoi${apache.poi.version}org.apache.poipoi-ooxml${apache.poi.version}net.bull.javamelodyjavamelody-core${javamelody.version}com.google.code.findbugsjsr305${findbugs.helper.version}org.apache.maven.pluginsmaven-project-info-reports-plugin${report.plugin.maven.inforeports}org.apache.maven.pluginsmaven-jxr-plugin${report.plugin.maven.jxr}org.apache.maven.pluginsmaven-surefire-report-plugin${report.plugin.maven.surefire}org.apache.maven.pluginsmaven-javadoc-plugin${report.plugin.maven.javadoc}${javadoc.opts}org.apache.maven.pluginsmaven-checkstyle-plugin${report.plugin.maven.checkstyle}checkstyleorg.codehaus.mojotaglist-maven-plugin${report.plugin.codehaus.taglist}org.apache.maven.pluginsmaven-pmd-plugin${report.plugin.maven.pmd}falseorg.codehaus.mojofindbugs-maven-plugin${report.plugin.codehaus.findbugs}org.codehaus.mojoversions-maven-plugin${report.plugin.codehaus.versions}dependency-updates-reportplugin-updates-reportproperty-updates-reportorg.apache.maven.pluginsmaven-site-plugin${build.plugin.maven.site}org.apache.maven.pluginsmaven-compiler-plugin${build.plugin.maven.compiler}${project.build.sourceEncoding}${maven.compiler.source}${maven.compiler.target}maven-deploy-plugin${build.plugin.maven.deploy}maven-clean-plugin${build.plugin.maven.clean}maven-install-plugin${build.plugin.maven.install}org.apache.tomcat.maventomcat7-maven-plugin${build.plugin.tomcat.maven.tomcat7}bothorg.apache.maven.pluginsmaven-dependency-plugin${build.plugin.maven.dependency}org.apache.maven.pluginsmaven-war-plugin${build.plugin.maven.war}trueorg.apache.maven.pluginsmaven-resources-plugin${build.plugin.maven.resources}${project.build.sourceEncoding}org.apache.maven.pluginsmaven-surefire-plugin${build.plugin.maven.surefire}${skipTests}-XX:-UseSplitVerifier
This will set most of your dependencies straight, \\
the parent pom ensures all projects use UTF-8 encoding for files & sets some maven plugins.
==== Possible extras ====
JSTL (Java Standard Tag Libraries), using an implementation which doesn't potentially break your server.
org.apache.taglibstaglibs-standard-spec1.2.5org.apache.taglibstaglibs-standard-impl1.2.5
Apache Tika (for textextraction purposes)
org.apache.tikatika-parsers1.14
Hibernate search (for textsearching purposes / index)
org.hibernatehibernate-search-orm5.7.0.Final
===== context.xml : =====
We're going to set some properties in META-INF/context.xml \\
if path = "''/''" it will deploy locally to your server root. To get the same result on a live server, you need to rename the war to ''ROOT.war''. \\
if path = "''/applicationname''" it will deploy locally to /applicationname. To get the same result on a live server, you need to rename the war to ''applicationname.war''.\\
\\
**webAppRootKey**: an identifier for the applciation, give it the value of your applicationname \\
**database_user**: make a database user like ''applicationname_user'', you can make the same one on the live server (with same password).\\
**database_catalog**: applicationname\\
**searchindexPath**: remove entry if not using hibernate search, change applicationname otherwise.\\
**uploadPath**: change applicationname, don't forget to create the path on server + locally. also make sure to ''chmod -R 777''
===== web.xml : =====
Now we need the following in our ''WEB-INF/web.xml'' \\
Don't forget to change applicationname.\\
applicationname
30
===== Java Configuration classes =====
==== Why? ====
We want to create a smaller application with less dependencies & less configuration (configuration by convention). \\
Properties which are app dependent can still be placed in META-INF/context.xml
==== AppConfiguration ====
package be.mentoringsystems.boardportal.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
*
* @author anthonyarents
*/
@Configuration
@ComponentScan({ "be.mentoringsystems.boardportal.*" })
@Import({ MvcConfiguration.class, DatabaseConfiguration.class, SecurityConfiguration.class })
public class AppConfiguration {
}
==== MvcConfiguration ====
package be.mentoringsystems.boardportal.config;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.http.MediaType;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
*
* @author anthonyarents
*/
@Configuration
@EnableWebMvc
public class MvcConfiguration extends WebMvcConfigurerAdapter {
@Override
public void configureDefaultServletHandling(final DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
@Override
public void configureContentNegotiation(final ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(false).favorParameter(true);
configurer.defaultContentType(MediaType.TEXT_PLAIN);
configurer.useJaf(false);
configurer.mediaType("txt", MediaType.TEXT_PLAIN);
configurer.mediaType("json", MediaType.APPLICATION_JSON);
configurer.mediaType("xml", MediaType.APPLICATION_XML);
configurer.mediaType("html", MediaType.TEXT_HTML);
}
@Override
public void configureMessageConverters(final List> converters) {
final Hibernate5Module hibernate5Module = new Hibernate5Module();
hibernate5Module.configure(Hibernate5Module.Feature.FORCE_LAZY_LOADING, false);
hibernate5Module.configure(Hibernate5Module.Feature.USE_TRANSIENT_ANNOTATION, false);
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.indentOutput(true);
builder.defaultViewInclusion(true);
builder.modules(hibernate5Module);
builder.dateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm")).timeZone("CET");
converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
final StringHttpMessageConverter stringMessageConverter = new StringHttpMessageConverter(Charset.forName("UTF-8"));
converters.add(stringMessageConverter);
converters.add(new MappingJackson2XmlHttpMessageConverter(builder.createXmlMapper(true).build()));
converters.add(new ByteArrayHttpMessageConverter());
}
@Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
// images
registry.addResourceHandler("/static/image/**").addResourceLocations("/static/image/").setCachePeriod(2678400);
// development boardportal
registry.addResourceHandler("/static/BoardPortal/**").addResourceLocations("/static/BoardPortal/").setCachePeriod(604800);
// production boardportal
//registry.addResourceHandler("/static/BoardPortal/**").addResourceLocations("/static/BoardPortal/build/production/BoardPortal/").setCachePeriod(604800);
//registry.addResourceHandler("/BoardPortal/**").addResourceLocations("/static/BoardPortal/build/production/BoardPortal/").setCachePeriod(604800);
//registry.addResourceHandler("/production/BoardPortal/**").addResourceLocations("/static/BoardPortal/build/production/BoardPortal/").setCachePeriod(604800);
super.addResourceHandlers(registry);
}
@Override
public void configureViewResolvers(final ViewResolverRegistry registry) {
registry.jsp("/WEB-INF/jsp/", ".jsp");
}
@Override
public void addViewControllers(final ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
// development boardportal
registry.addRedirectViewController("/", "/static/BoardPortal/");
}
@Bean(name = "multipartResolver")
public StandardServletMultipartResolver multipartResolver() {
return new StandardServletMultipartResolver();
}
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
}
==== DatabaseConfiguration ====
package be.mentoringsystems.boardportal.config;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
*
* @author anthonyarents
*/
@Configuration
@EnableTransactionManagement
public class DatabaseConfiguration {
@Value("#{contextParameters.database_catalog}")
private String dbName;
@Value("#{contextParameters.database_user}")
private String dbUser;
@Value("#{contextParameters.database_password}")
private String dbPass;
@Value("#{contextParameters.database_jdbc}")
private String dbJdbc;
@Value("#{contextParameters.searchindexPath}")
private String searchindexPath;
@Bean
public DataSource dataSource() {
final BasicDataSource bds = new BasicDataSource();
bds.setUsername(dbUser);
bds.setPassword(dbPass);
bds.setDefaultCatalog(dbName);
bds.setUrl(dbJdbc);
bds.setDriverClassName("com.mysql.jdbc.Driver");
bds.setMaxIdle(17);
bds.setMaxTotal(20);
bds.setMaxWaitMillis(10000);
bds.setValidationQuery("SELECT 1;");
bds.setTestOnBorrow(true);
bds.setRemoveAbandonedOnBorrow(true);
return bds;
}
private Properties getHibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
properties.put("hibernate.show_sql", false);
properties.put("hibernate.format_sql", false);
properties.put("hibernate.connection.useUnicode", true);
properties.put("hibernate.connection.characterEncoding", "utf8");
properties.put("hibernate.connection.charSet", "utf8");
properties.put("hibernate.search.default.directory_provider", "filesystem");
properties.put("hibernate.search.default.indexBase", searchindexPath);
return properties;
}
@Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setAnnotatedPackages(new String[]{"be.mentoringsystems.boardportal.model.db"});
sessionFactory.setPackagesToScan(new String[]{"be.mentoringsystems.boardportal.model.db"});
sessionFactory.setHibernateProperties(getHibernateProperties());
return sessionFactory;
}
@Bean
@Autowired
public HibernateTransactionManager transactionManager(final SessionFactory s) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(s);
return txManager;
}
}
==== SecurityConfiguration ====
package be.mentoringsystems.boardportal.config;
import be.mentoringsystems.boardportal.service.LoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.encoding.Md5PasswordEncoder;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
*
* @author anthonyarents
*/
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{
@Autowired
private LoginService loginService;
@Override
public void configure(final WebSecurity web) throws Exception {
web.ignoring().antMatchers("/static/**", "/app/**");
}
@Override
protected void configure(final HttpSecurity http) throws Exception {
// temporary
http.csrf().disable();
http.headers().frameOptions().sameOrigin();
http.userDetailsService(loginService);
http
.formLogin()
.loginPage("/login").defaultSuccessUrl("/", true).failureUrl("/login?error").permitAll() //login page, redirect tologinrequired page if there was one, permitall is necessary
.and()
.logout().logoutUrl("/logout").permitAll() // logout page, permitall is necessary
.and()
.authorizeRequests() // request matching with ant
.antMatchers("/login/**").permitAll() // allows for login to have more get requests (localization)
.antMatchers("/register").permitAll()
.antMatchers("/event/**").permitAll()
.anyRequest().authenticated(); // all remaining requests need login
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
Md5PasswordEncoder passwordEncoder = new Md5PasswordEncoder();
auth.userDetailsService(loginService).passwordEncoder(passwordEncoder);
}
}
==== Spring Webapplication Initializer ====
removes web.xml config: the servlet code
package be.mentoringsystems.boardportal.config.initializer;
import be.mentoringsystems.boardportal.config.AppConfiguration;
import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletRegistration;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
/**
*
* @author anthonyarents
*/
public class SpringConfigurationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class>[] getRootConfigClasses() {
return new Class[] { AppConfiguration.class };
}
@Override
protected Class>[] getServletConfigClasses() {
return null;
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
@Override
protected void customizeRegistration(ServletRegistration.Dynamic registration) {
MultipartConfigElement multipartConfigElement = new MultipartConfigElement("/tmp", parseSize("50MB"), parseSize("70MB"), 0);
registration.setMultipartConfig(multipartConfigElement);
}
// convience to make this config a bit more readable...
private long parseSize(String size) {
size = size.toUpperCase();
if (size.endsWith("KB")) {
return Long.valueOf(size.substring(0, size.length() - 2)) * 1024;
}
if (size.endsWith("MB")) {
return Long.valueOf(size.substring(0, size.length() - 2)) * 1024 * 1024;
}
return Long.valueOf(size);
}
}
==== Spring Security Initializer ====
removes web.xml config: filterchain
package be.mentoringsystems.boardportal.config.initializer;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
/**
*
* @author anthonyarents
*/
public class SpringSecurityInitializer extends AbstractSecurityWebApplicationInitializer {
}
===== Optional Java Configuration classes =====
These are examples of extra configuration. The class needs to be added to AppConfiguration's @Import !
==== Mail configuration ====
used for configuring a javamailer
package be.mentoringsystems.merke.config;
import java.util.Properties;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl;
/**
*
* @author anthonyarents
*/
@Configuration
public class MailConfiguration {
@Value("#{contextParameters.email_host}")
private String host;
@Value("#{contextParameters.email_user}")
private String user;
@Value("#{contextParameters.email_pass}")
private String pass;
@Value("#{contextParameters.email_port}")
private Integer port;
@Value("#{contextParameters.email_auth}")
private Boolean auth;
@Value("#{contextParameters.email_starttls}")
private Boolean starttls;
@Bean(name = "javaMailService")
public JavaMailSender javaMailService() {
JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
javaMailSender.setDefaultEncoding("UTF-8");
javaMailSender.setHost(host);
javaMailSender.setUsername(user);
javaMailSender.setPassword(pass);
javaMailSender.setPort(port);
final Properties properties = new Properties();
properties.setProperty("mail.smtp.auth", Boolean.toString(auth));
properties.setProperty("mail.smtp.starttls.enable", Boolean.toString(starttls));
javaMailSender.setJavaMailProperties(properties);
return javaMailSender;
}
}
==== Message configuration ====
used for translation messages
package be.mentoringsystems.merke.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
/**
*
* @author anthonyarents
*/
@Configuration
public class MessageConfiguration {
@Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource source = new ResourceBundleMessageSource();
source.setBasename("i18n/messages");
source.setUseCodeAsDefaultMessage(true);
return source;
}
}
==== Async configuration ====
used for async method configuration, with this it's possible to use @Async for methods you want to run async \\ (like the method in charge of sending a mail).
package be.mentoringsystems.merke.config;
import java.util.concurrent.Executor;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
/**
*
* @author anthonyarents
*/
@Configuration
@EnableAsync
public class AsyncConfiguration extends AsyncConfigurerSupport {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("Mail-");
executor.setAwaitTerminationSeconds(10);
executor.initialize();
return executor;
}
}
====== Model - Service - DAO - Presentation ======
===== QueryParams =====
package be.mentoringsystems.merke.model;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
/**
*
* @author anthonyarents
*/
public class QueryParams {
private int start = 0;
private int limit = 0;
private FilterMap filter = new FilterMap();
private SortingMap sort = new SortingMap();
private List initializeExtra = new ArrayList();
public List getInitializeExtra() {
return initializeExtra;
}
public void setInitializeExtra(final List initializeExtra) {
this.initializeExtra = initializeExtra;
}
public void addInitializeExtra(final String property) {
this.initializeExtra.add(property);
}
public FilterMap getFilter() {
return filter;
}
public void setFilter(final FilterMap filter) {
this.filter = filter;
}
@JsonAnySetter
public void addFilter(final String property, final Object value) {
if (!StringUtils.isEmpty(property) && !StringUtils.startsWith(property, "_")) {
Filter filter = getFilter(property);
if (filter == null) {
filter = new Filter();
filter.setProperty(property);
filter.setValue(value);
this.filter.addFilter(filter);
} else {
filter.setValue(value);
}
}
}
public Filter getFilter(final String property) {
if (this.getFilter() == null) {
return null;
} else {
return this.filter.getFilter(property);
}
}
public Object getFilterValue(final String property) {
if (this.getFilter() == null) {
return null;
} else {
return this.getFilter().getValue(property);
}
}
public int getLimit() {
return limit;
}
public void setLimit(final int limit) {
this.limit = limit;
}
public SortingMap getSort() {
return sort;
}
public void setSort(final SortingMap sort) {
this.sort = sort;
}
public int getStart() {
return start;
}
public void setStart(final int start) {
this.start = start;
}
}
==== FilterMap ====
package be.mentoringsystems.merke.model;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
* @author anthonyarents
*/
public class FilterMap {
private List filters = new ArrayList();
private Map map = new HashMap();
public List getFilters() {
return filters;
}
public void setFilters(final List filters) {
this.filters = filters;
}
public void addFilter(final Filter filter) {
this.filters.add(filter);
this.map.put(filter.getProperty(), filter);
}
public Map getMap() {
return map;
}
public void setMap(Map map) {
this.map = map;
}
public Filter getFilter(final String property) {
return map.get(property);
}
public Object getValue(final String property) {
final Filter result = getFilter(property);
if(result == null) {
return null;
} else {
return result.getValue();
}
}
}
==== Filter ====
package be.mentoringsystems.merke.model;
/**
*
* @author anthonyarents
*/
public class Filter {
private String property;
private Object value;
public String getProperty() {
return property;
}
public void setProperty(String property) {
this.property = property;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}
==== SortingMap ====
package be.mentoringsystems.merke.model;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author anthonyarents
*/
public class SortingMap {
private List sortings = new ArrayList<>();
public List getSortings() {
return sortings;
}
public void setSortings(final List sortings) {
this.sortings = sortings;
}
}
==== Sorting ====
package be.mentoringsystems.merke.model;
/**
*
* @author anthonyarents
*/
public class Sorting {
private String property;
private String direction;
public String getDirection() {
return direction;
}
public void setDirection(String direction) {
this.direction = direction;
}
public String getProperty() {
return property;
}
public void setProperty(String property) {
this.property = property;
}
}
===== Helpers =====
package be.mentoringsystems.merke.helper;
import java.io.Serializable;
import java.util.UUID;
import org.hibernate.HibernateException;
import org.hibernate.engine.SessionImplementor;
/**
*
* @author anthonyarents
*/
public class UUIDTypeGenerator extends org.hibernate.id.GUIDGenerator {
public UUIDTypeGenerator() {
super();
}
@Override
public Serializable generate(SessionImplementor session, Object obj) throws HibernateException {
String result = (String) super.generate(session, obj);
return UUID.fromString(result);
}
}
package be.mentoringsystems.merke.helper;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import javax.annotation.Nullable;
import org.apache.commons.lang3.math.NumberUtils;
/**
*
* @author anthonyarents
*/
public final class ConversionHelper {
private ConversionHelper() {
}
@SuppressWarnings("unchecked")
public static Listpackage be.mentoringsystems.merke.helper;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
/**
*
* @author anthonyarents
*/
public class DTOHelper {
public static void copyNonNull(final Object source, final Object target) {
try {
Map sourceValues = PropertyUtils.describe(source);
Collection values = sourceValues.values();
values.removeAll(Collections.singleton(null));
populate(sourceValues, target);
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException ex) {
Logger.getLogger(DTOHelper.class.getName()).log(Level.SEVERE, null, ex);
}
}
public static void copy(final Object source, final Object target) {
try {
Map sourceValues = PropertyUtils.describe(source);
populate(sourceValues, target);
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException ex) {
Logger.getLogger(DTOHelper.class.getName()).log(Level.SEVERE, null, ex);
}
}
public static void populate(final Map source, final Object target) throws IllegalAccessException, InvocationTargetException {
BeanUtils.populate(target, source);
}
}
===== Model =====
Of note : \\
* implements UserDetails (Spring Security)
* Uses JsonView, IF we want to have a specific view, we want to mark all "public" fields with the "View.Public"\\ class and the "private" fields with a class which extends "View.Public" class, preferably "View."
package be.mentoringsystems.merke.model.db;
import be.mentoringsystems.merke.model.dto.View;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonView;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;
import org.hibernate.annotations.Type;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
/**
*
* @author anthonyarents
*/
@Entity
public class Login implements UserDetails, Serializable {
private static final long serialVersionUID = -5435867605493271271L;
private UUID id;
private String firstname;
private String lastname;
private String email;
private String password;
private Integer group = 2;
private String username;
private boolean deleted = false;
private boolean active = false;
private Date createdOn = new Date();
private List authorities = new ArrayList();
@JsonIgnore
@Column
@Type(type = "yes_no")
public boolean isDeleted() {
return deleted;
}
public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
@JsonView(View.Public.class)
@Id
@Column
@GeneratedValue( generator = "uuid" )
@Type(type = "uuid-char")
public UUID getId() {
return id;
}
public void setId(final UUID id) {
this.id = id;
}
@JsonView(View.Login.class)
@Column
@Temporal(TemporalType.TIMESTAMP)
public Date getCreatedOn() {
return (createdOn == null ? null : new Date(createdOn.getTime()));
}
public void setCreatedOn(Date createdOn) {
this.createdOn = (createdOn == null ? null : new Date(createdOn.getTime()));
}
@JsonView(View.Login.class)
@Column
public String getEmail() {
return email;
}
public void setEmail(final String email) {
this.email = email;
}
@JsonView(View.Public.class)
@Column
public String getFirstname() {
return firstname;
}
public void setFirstname(final String firstname) {
this.firstname = firstname;
}
@JsonView(View.Login.class)
@Column(name="`group`", nullable = false)
public Integer getGroup() {
return group;
}
public void setGroup(Integer group) {
this.group = group;
}
@JsonView(View.Public.class)
@Column
public String getLastname() {
return lastname;
}
public void setLastname(final String lastname) {
this.lastname = lastname;
}
@JsonView(View.Login.class)
@Column
@Override
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@JsonIgnore
@Column(nullable = false)
@Override
public String getPassword() {
return password;
}
public void setPassword(final String password) {
this.password = password;
}
@JsonView(View.Login.class)
@Column
@Type(type = "yes_no")
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
@JsonIgnore
@Override
@Transient
public Collection extends GrantedAuthority> getAuthorities() {
return authorities;
}
public void setAuthorities(List authorities) {
this.authorities = authorities;
}
@JsonIgnore
@Override
@Transient
public boolean isAccountNonExpired() {
return true;
}
@JsonIgnore
@Override
@Transient
public boolean isAccountNonLocked() {
return true;
}
@JsonIgnore
@Override
@Transient
public boolean isCredentialsNonExpired() {
return true;
}
@JsonIgnore
@Override
@Transient
public boolean isEnabled() {
return this.isActive();
}
}
Important : "package-info.java" with uuid generator, to be added to model.db
@GenericGenerator(
name = "uuid",
strategy = "be.mentoringsystems.helper.UUIDTypeGenerator"
)
@FilterDef(name="deletedObjectsFilter", defaultCondition="deleted = 'N'")
package be.mentoringsystems.merke.model.db;
import org.hibernate.annotations.FilterDef;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;
===== DAO Interface =====
Now we could use a DAO interface
package be.mentoringsystems.boardportal.persistence;
import be.mentoringsystems.boardportal.model.db.Login;
import java.util.List;
/**
*
* @author anthonyarents
*/
public interface LoginDAO {
void save(final Login obj);
Login getById(final UUID id);
Login getByUsername(final String username);
List getAll(final QueryParams queryParams);
Long getCount(final QueryParams queryParams);
}
package be.mentoringsystems.merke.persistence;
import be.mentoringsystems.merke.model.QueryParams;
import be.mentoringsystems.merke.model.db.Vendor;
import java.util.List;
import java.util.UUID;
/**
*
* @author anthonyarents
*/
public interface VendorDAO {
void save(final Vendor obj);
Vendor getById(final UUID id);
List getAll(final QueryParams queryParams);
Long getCount(final QueryParams queryParams);
}
===== DAO Implementation =====
Superclass
package be.mentoringsystems.merke.persistence.hibernate;
import be.mentoringsystems.merke.model.Filter;
import be.mentoringsystems.merke.model.QueryParams;
import be.mentoringsystems.merke.model.Sorting;
import java.util.List;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.springframework.orm.hibernate5.support.HibernateDaoSupport;
/**
*
* @author anthonyarents
*/
public class HibernateDAO extends HibernateDaoSupport {
private static final String SORT_DESC = "DESC";
protected boolean addSorting(final DetachedCriteria criteria, final QueryParams queryParams) {
if (queryParams.getSort() == null) {
return false;
} else {
for (Sorting sorting : queryParams.getSort().getSortings()) {
addOrder(criteria, sorting);
}
return true;
}
}
protected void addOrder(final DetachedCriteria criteria, Sorting sorting) {
criteria.addOrder(getOrder(sorting.getDirection(), sorting.getProperty()));
}
protected Order getOrder(final String dir, final String property) {
if (SORT_DESC.equals(dir)) {
return Order.desc(property);
} else {
return Order.asc(property);
}
}
protected boolean addFilter(final DetachedCriteria criteria, final QueryParams queryParams) {
if (queryParams.getFilter() == null) {
return false;
} else {
for (Filter filter : queryParams.getFilter().getFilters()) {
if (filter.getValue() == null) {
criteria.add(Restrictions.isNull(filter.getProperty()));
} else {
criteria.add(Restrictions.eq(filter.getProperty(), filter.getValue()));
}
}
return true;
}
}
List> byCriteria(final DetachedCriteria criteria, final QueryParams queryParams) {
return getHibernateTemplate().findByCriteria(criteria, queryParams.getStart(), queryParams.getLimit());
}
List> byCriteriaS(final DetachedCriteria criteria, final QueryParams queryParams) {
addSorting(criteria, queryParams);
return getHibernateTemplate().findByCriteria(criteria, queryParams.getStart(), queryParams.getLimit());
}
List> byCriteriaF(final DetachedCriteria criteria, final QueryParams queryParams) {
addFilter(criteria, queryParams);
return getHibernateTemplate().findByCriteria(criteria, queryParams.getStart(), queryParams.getLimit());
}
List> byCriteriaFS(final DetachedCriteria criteria, final QueryParams queryParams) {
addFilter(criteria, queryParams);
addSorting(criteria, queryParams);
return getHibernateTemplate().findByCriteria(criteria, queryParams.getStart(), queryParams.getLimit());
}
}
A Hibernate DAO implementation
package be.mentoringsystems.merke.persistence.hibernate;
import be.mentoringsystems.merke.helper.ConversionHelper;
import be.mentoringsystems.merke.model.QueryParams;
import be.mentoringsystems.merke.model.db.Login;
import be.mentoringsystems.merke.persistence.LoginDAO;
import java.util.List;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
/**
*
* @author anthonyarents
*/
@Repository
@SuppressWarnings("unchecked")
public class LoginDAOImpl extends HibernateDAO implements LoginDAO {
@Override
public void save(final Login obj) {
getHibernateTemplate().saveOrUpdate(obj);
}
@Override
public Login getById(final UUID id) {
final Login result = getHibernateTemplate().get(Login.class, id);
return result;
}
@Override
public Login getByUsername(final String username) {
final DetachedCriteria criteria = DetachedCriteria.forClass(Login.class);
criteria.add(Restrictions.eq("deleted", false));
criteria.add(Restrictions.eq("username", username));
List result = (List) getHibernateTemplate().findByCriteria(criteria);
if(result.isEmpty()) {
return null;
} else {
return result.get(0);
}
}
private void addFilters(final DetachedCriteria criteria, final QueryParams queryParams) {
final String search = ConversionHelper.toString(queryParams.getFilterValue("search"));
if(!StringUtils.isEmpty(search)) {
criteria.add(
Restrictions.or(
Restrictions.ilike("firstname", search, MatchMode.ANYWHERE),
Restrictions.ilike("lastname", search, MatchMode.ANYWHERE),
Restrictions.ilike("username", search, MatchMode.ANYWHERE)
));
}
}
@Override
public List getAll(final QueryParams queryParams) {
final DetachedCriteria criteria = DetachedCriteria.forClass(Login.class);
criteria.add(Restrictions.eq("deleted", false));
addFilters(criteria, queryParams);
addSorting(criteria, queryParams);
return (List) getHibernateTemplate().findByCriteria(criteria, queryParams.getStart(), queryParams.getLimit());
}
@Override
public Long getCount(final QueryParams queryParams) {
final DetachedCriteria criteria = DetachedCriteria.forClass(Login.class);
criteria.add(Restrictions.eq("deleted", false));
addFilters(criteria, queryParams);
criteria.setProjection(Projections.rowCount());
List result = (List) getHibernateTemplate().findByCriteria(criteria);
return result.get(0);
}
@Autowired
public void init(SessionFactory factory) {
setSessionFactory(factory);
}
}
package be.mentoringsystems.merke.persistence.hibernate;
import be.mentoringsystems.merke.helper.ConversionHelper;
import be.mentoringsystems.merke.model.QueryParams;
import be.mentoringsystems.merke.model.db.Vendor;
import be.mentoringsystems.merke.model.db.Vendorstall;
import be.mentoringsystems.merke.persistence.VendorDAO;
import java.util.List;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate5.HibernateTemplate;
import org.springframework.stereotype.Repository;
/**
*
* @author anthonyarents
*/
@Repository
@SuppressWarnings("unchecked")
public class VendorDAOImpl extends HibernateDAO implements VendorDAO {
private void initializeDependencies(final HibernateTemplate template, final List list) {
for(Vendor obj : list) {
initializeDependencies(template, obj);
}
}
private void initializeDependencies(final HibernateTemplate template, final Vendor obj) {
template.initialize(obj.getVendorstalls());
for(Vendorstall vendorstall : obj.getVendorstalls()) {
template.initialize(vendorstall.getCertificates());
}
template.initialize(obj.getRemarks());
}
@Autowired
public void init(SessionFactory factory) {
setSessionFactory(factory);
}
@Override
public void save(Vendor obj) {
getHibernateTemplate().saveOrUpdate(obj);
}
@Override
public Vendor getById(UUID id) {
final HibernateTemplate template = getHibernateTemplate();
final Vendor result = template.get(Vendor.class, id);
initializeDependencies(template, result);
return result;
}
private void addFilters(DetachedCriteria criteria, QueryParams queryParams) {
final List years = ConversionHelper.toIntegerList(queryParams.getFilterValue("years"));
final List venueIds = ConversionHelper.toUUIDList(queryParams.getFilterValue("venueIds"));
final List categories = ConversionHelper.toUUIDList(queryParams.getFilterValue("categoryIds"));
final List remarktypes = ConversionHelper.toStringList(queryParams.getFilterValue("remarktypes"));
final String search = ConversionHelper.toString(queryParams.getFilterValue("search"));
boolean vendorstalljoin = false;
if(!StringUtils.isEmpty(search)) {
criteria.add(Restrictions.or(
Restrictions.ilike("firstname", search, MatchMode.START),
Restrictions.ilike("lastname", search, MatchMode.START),
Restrictions.ilike("companyName", search, MatchMode.START)
));
}
if(!venueIds.isEmpty()) {
if(!vendorstalljoin) {
criteria.createAlias("vendorstalls", "vstall");
vendorstalljoin = true;
}
criteria.createAlias("vstall.venues", "ven");
criteria.add(Restrictions.in("ven.id", venueIds));
}
if(!categories.isEmpty()) {
if(!vendorstalljoin) {
criteria.createAlias("vendorstalls", "vstall");
vendorstalljoin = true;
}
criteria.createAlias("vstall.categories", "cat");
criteria.add(Restrictions.in("cat.id", categories));
}
if(!remarktypes.isEmpty()) {
criteria.createAlias("remarks", "rem");
criteria.add(Restrictions.in("rem.category", remarktypes));
}
if(!years.isEmpty()) {
final StringBuilder inBuilder = new StringBuilder("YEAR(createdOn) IN (").append(StringUtils.join(years, ",")).append(")");
criteria.add(Restrictions.sqlRestriction(inBuilder.toString()));
}
}
@Override
public List getAll(QueryParams queryParams) {
final HibernateTemplate template = getHibernateTemplate();
final DetachedCriteria criteria = DetachedCriteria.forClass(Vendor.class);
criteria.add(Restrictions.eq("deleted", false));
addFilters(criteria, queryParams);
addSorting(criteria, queryParams);
final List result = (List) template.findByCriteria(criteria, queryParams.getStart(), queryParams.getLimit());
initializeDependencies(template, result);
return result;
}
@Override
public Long getCount(QueryParams queryParams) {
final DetachedCriteria criteria = DetachedCriteria.forClass(Vendor.class);
criteria.add(Restrictions.eq("deleted", false));
addFilters(criteria, queryParams);
criteria.setProjection(Projections.rowCount());
final List result = (List) getHibernateTemplate().findByCriteria(criteria);
return result.get(0);
}
}
Advanced hibernate criteria queries exist, If you need examples contact me :-).
===== Service Interface =====
Of note : extends UserDetailsService (allows for custom spring security login)\\
package be.mentoringsystems.merke.service;
import be.mentoringsystems.merke.model.QueryParams;
import be.mentoringsystems.merke.model.db.Login;
import be.mentoringsystems.merke.model.dto.LoginDTO;
import be.mentoringsystems.merke.model.dto.PagedListDTO;
import java.util.List;
import java.util.UUID;
import org.springframework.security.core.userdetails.UserDetailsService;
/**
*
* @author anthonyarents
*/
public interface LoginService extends UserDetailsService {
void save(final Login obj);
Login save(final LoginDTO dto);
void delete(final Login obj);
void delete(final UUID id);
Login getById(final UUID id);
Login getByUsername(final String login);
List getAll(final QueryParams queryParams);
Long getCount(final QueryParams queryParams);
PagedListDTO getAllDTO(final QueryParams queryParams);
Login getCurrentLogin();
Login getSourceLogin();
}
package be.mentoringsystems.merke.service;
import be.mentoringsystems.merke.model.QueryParams;
import be.mentoringsystems.merke.model.db.Vendor;
import be.mentoringsystems.merke.model.dto.PagedListDTO;
import be.mentoringsystems.merke.model.dto.VendorDTO;
import java.util.List;
import java.util.UUID;
/**
*
* @author anthonyarents
*/
public interface VendorService {
void save(final Vendor obj);
Vendor save(final VendorDTO dto);
void delete(final Vendor obj);
void delete(final UUID id);
Vendor getById(final UUID id);
List getAll(final QueryParams queryParams);
Long getCount(final QueryParams queryParams);
PagedListDTO getAllDTO(final QueryParams queryParams);
}
===== Service Implementation =====
package be.mentoringsystems.merke.service.impl;
import be.mentoringsystems.merke.helper.DTOHelper;
import be.mentoringsystems.merke.model.QueryParams;
import be.mentoringsystems.merke.model.db.Login;
import be.mentoringsystems.merke.model.dto.LoginDTO;
import be.mentoringsystems.merke.model.dto.PagedListDTO;
import be.mentoringsystems.merke.persistence.LoginDAO;
import be.mentoringsystems.merke.service.LoginService;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.switchuser.SwitchUserGrantedAuthority;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
/**
*
* @author anthonyarents
*/
@Service
@Transactional(propagation = Propagation.SUPPORTS)
public class LoginServiceImpl implements LoginService {
@Autowired
private transient LoginDAO loginDAO;
private static final Logger LOGGER = LogManager.getLogger(LoginServiceImpl.class);
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void save(final Login login) {
loginDAO.save(login);
}
@Override
@Transactional(propagation = Propagation.REQUIRED)
public Login save(final LoginDTO dto) {
Login obj;
if(dto.getId() == null) {
obj = new Login();
LOGGER.info("Creating a new Login");
} else {
obj = getById(dto.getId());
LOGGER.info("Updating existing Login({})", dto.getId());
}
String dtoPassword = dto.getPassword();
dto.setPassword(null);
DTOHelper.copyNonNull(dto, obj);
if(StringUtils.hasText(dtoPassword)) obj.setPassword(encryptPassword(dtoPassword));
save(obj);
LOGGER.info("Login({}) saved", obj.getId());
return obj;
}
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void delete(final Login login) {
login.setDeleted(true);
save(login);
LOGGER.info("Login({}) deleted", login.getId());
}
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void delete(final UUID id) {
final Login login = getById(id);
delete(login);
}
@Override
@Transactional(readOnly = true)
public Login getById(final UUID id) {
return loginDAO.getById(id);
}
@Override
@Transactional(readOnly = true)
public Login getByUsername(final String username) {
return loginDAO.getByUsername(username);
}
@Override
@Transactional(readOnly = true)
public List getAll(final QueryParams queryParams) {
return loginDAO.getAll(queryParams);
}
@Override
@Transactional(readOnly = true)
public Long getCount(final QueryParams queryParams) {
return loginDAO.getCount(queryParams);
}
@Override
@Transactional(readOnly = true)
public PagedListDTO getAllDTO(final QueryParams queryParams) {
final PagedListDTO pagedListDTO = new PagedListDTO();
pagedListDTO.setData(getAll(queryParams));
pagedListDTO.setTotal(getCount(queryParams));
return pagedListDTO;
}
public String encryptPassword(final String password) {
String result = password;
try {
result = DigestUtils.md5DigestAsHex(password.getBytes("UTF-8"));
} catch (UnsupportedEncodingException ex) {
LOGGER.error(ex.getMessage(), ex);
}
return result;
}
@Override
@Transactional(readOnly = true)
public UserDetails loadUserByUsername(final String loginName) throws UsernameNotFoundException {
final Login login = getByUsername(loginName);
if(login == null) {
throw new UsernameNotFoundException(loginName);
} else {
login.setAuthorities(getAuthorities(login));
return login;
}
}
public List getAuthorities(final Login login) {
final List authorities = new ArrayList<>();
if(login.getGroup() == 1) {
authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
}
if(login.getGroup() == 3) {
authorities.add(new SimpleGrantedAuthority("ROLE_FIREMAN"));
}
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
return authorities;
}
@Override
public Login getCurrentLogin() {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if(principal instanceof Login) {
return (Login) principal;
}
return null;
}
@Override
public Login getSourceLogin() {
final Login current = getCurrentLogin();
Login original = null;
for (GrantedAuthority auth : current.getAuthorities()) {
if (auth instanceof SwitchUserGrantedAuthority) {
Authentication authSource = ((SwitchUserGrantedAuthority) auth).getSource();
if(authSource instanceof Login) original = (Login) authSource;
}
}
return original;
}
}
package be.mentoringsystems.merke.service.impl;
import be.mentoringsystems.merke.helper.DTOHelper;
import be.mentoringsystems.merke.model.QueryParams;
import be.mentoringsystems.merke.model.db.Vendor;
import be.mentoringsystems.merke.model.dto.PagedListDTO;
import be.mentoringsystems.merke.model.dto.VendorDTO;
import be.mentoringsystems.merke.persistence.VendorDAO;
import be.mentoringsystems.merke.service.VendorService;
import java.util.List;
import java.util.UUID;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
*
* @author anthonyarents
*/
@Service
@Transactional(propagation = Propagation.SUPPORTS)
public class VendorServiceImpl implements VendorService {
@Autowired
private transient VendorDAO vendorDAO;
private static final Logger LOGGER = LogManager.getLogger(VendorServiceImpl.class);
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void save(Vendor obj) {
vendorDAO.save(obj);
}
@Override
@Transactional(propagation = Propagation.REQUIRED)
public Vendor save(VendorDTO dto) {
Vendor obj;
if(dto.getId() == null) {
obj = new Vendor();
LOGGER.info("Creating a new Vendor");
} else {
obj = getById(dto.getId());
LOGGER.info("Updating existing Vendor({})", dto.getId());
}
DTOHelper.copyNonNull(dto, obj);
save(obj);
LOGGER.info("Vendor({}) saved", obj.getId());
return obj;
}
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void delete(Vendor obj) {
obj.setDeleted(true);
save(obj);
LOGGER.info("Vendor({}) deleted", obj.getId());
}
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void delete(UUID id) {
final Vendor obj = getById(id);
delete(obj);
}
@Override
@Transactional(readOnly = true)
public Vendor getById(UUID id) {
return vendorDAO.getById(id);
}
@Override
@Transactional(readOnly = true)
public List getAll(final QueryParams queryParams) {
return vendorDAO.getAll(queryParams);
}
@Override
@Transactional(readOnly = true)
public Long getCount(final QueryParams queryParams) {
return vendorDAO.getCount(queryParams);
}
@Override
@Transactional(readOnly = true)
public PagedListDTO getAllDTO(final QueryParams queryParams) {
final PagedListDTO result = new PagedListDTO();
result.setData(getAll(queryParams));
result.setTotal(getCount(queryParams));
return result;
}
}
===== View =====
Jsonview values, notice we'll be using public as a default.
package be.mentoringsystems.merke.model.dto;
/**
*
* @author anthonyarents
*/
public class View {
public interface Public {};
public interface Vendor extends Public {};
public interface Login extends Public {};
public interface Venue extends Public {};
public interface Certificate extends Public {};
}
===== Data Transfer Objects (DTO) =====
We're using DTO objects to have more control of incoming data.
package be.mentoringsystems.merke.model.dto;
import java.util.UUID;
/**
*
* @author anthonyarents
*/
public class LoginDTO {
private UUID id;
private String firstname;
private String lastname;
private String email;
private String password;
private Integer group;
private String username;
private Boolean active;
public Boolean getActive() {
return active;
}
public void setActive(Boolean active) {
this.active = active;
}
public UUID getId() {
return id;
}
public void setId(final UUID id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(final String email) {
this.email = email;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(final String firstname) {
this.firstname = firstname;
}
public Integer getGroup() {
return group;
}
public void setGroup(Integer group) {
this.group = group;
}
public String getLastname() {
return lastname;
}
public void setLastname(final String lastname) {
this.lastname = lastname;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(final String password) {
this.password = password;
}
}package be.mentoringsystems.merke.model.dto;
import java.util.Date;
import java.util.UUID;
/**
*
* @author anthonyarents
*/
public class VendorDTO {
private UUID id;
private UUID parentid;
private UUID loginId;
private String title;
private String title2;
private String firstname;
private String lastname;
private String idnrNationalRegister;
private String street;
private String number;
private String postalcode;
private String city;
private String companyName;
private String companyNumber;
private String bankaccount;
private String telephonenumber;
private String email;
private String mobilenumber;
private Date birthdate;
private String permitNumber;
private String status;
private String change;
public String getTitle2() {
return title2;
}
public void setTitle2(String title2) {
this.title2 = title2;
}
public String getChange() {
return change;
}
public void setChange(String change) {
this.change = change;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getPermitNumber() {
return permitNumber;
}
public void setPermitNumber(String permitNumber) {
this.permitNumber = permitNumber;
}
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public UUID getLoginId() {
return loginId;
}
public void setLoginId(UUID loginId) {
this.loginId = loginId;
}
public UUID getParentid() {
return parentid;
}
public void setParentid(UUID parentid) {
this.parentid = parentid;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public String getIdnrNationalRegister() {
return idnrNationalRegister;
}
public void setIdnrNationalRegister(String idnrNationalRegister) {
this.idnrNationalRegister = idnrNationalRegister;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public String getPostalcode() {
return postalcode;
}
public void setPostalcode(String postalcode) {
this.postalcode = postalcode;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
public String getCompanyNumber() {
return companyNumber;
}
public void setCompanyNumber(String companyNumber) {
this.companyNumber = companyNumber;
}
public String getBankaccount() {
return bankaccount;
}
public void setBankaccount(String bankaccount) {
this.bankaccount = bankaccount;
}
public String getTelephonenumber() {
return telephonenumber;
}
public void setTelephonenumber(String telephonenumber) {
this.telephonenumber = telephonenumber;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Date getBirthdate() {
return (birthdate == null ? null : new Date(birthdate.getTime()));
}
public void setBirthdate(Date birthdate) {
this.birthdate = (birthdate == null ? null : new Date(birthdate.getTime()));
}
public String getMobilenumber() {
return mobilenumber;
}
public void setMobilenumber(String mobilenumber) {
this.mobilenumber = mobilenumber;
}
}
It's also useful for outgoing data :
package be.mentoringsystems.boardportal.model.dto;
import com.fasterxml.jackson.annotation.JsonInclude;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author anthonyarents
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ApiDTO {
private Boolean success = true;
private Object data;
private List messages = new ArrayList();
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public List getMessages() {
return messages;
}
public void setMessages(List messages) {
this.messages = messages;
}
public void addMessage(String message) {
this.messages.add(message);
}
public Boolean getSuccess() {
return success;
}
public void setSuccess(Boolean success) {
this.success = success;
}
public ApiDTO(Object data) {
this.data = data;
}
public ApiDTO() {
}
public static ApiDTO error() {
final ApiDTO apiDto = new ApiDTO();
apiDto.setSuccess(Boolean.FALSE);
return apiDto;
}
public static ApiDTO error(List messages) {
final ApiDTO apiDto = new ApiDTO();
apiDto.setSuccess(Boolean.FALSE);
apiDto.setMessages(messages);
return apiDto;
}
}
and paging :
package be.mentoringsystems.boardportal.model.dto;
import com.fasterxml.jackson.annotation.JsonInclude;
import java.util.List;
/**
*
* @author anthonyarents
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
public class PagedListDTO {
private List> data;
private Long total;
private Boolean success = true;
public List> getData() {
return data;
}
public void setData(final List> data) {
this.data = data;
}
public Boolean getSuccess() {
return success;
}
public void setSuccess(final Boolean success) {
this.success = success;
}
public Long getTotal() {
return total;
}
public void setTotal(final Long total) {
this.total = total;
}
public void setTotal(final int total) {
this.setTotal(new Long(total));
}
}
===== Presentation (Ajax, MultiActionController) =====
use requestmapping to map based on action parameter
package de.jcpis.analyse.presentation;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;
/**
*
* @author anthonyarents
*/
@Controller
@RequestMapping("/bankverbindung.json")
public class BankverbindungController {
@RequestMapping(params = "action=validateBankverbindung")
public ModelAndView validateBankverbindung(final WebRequest hsr) {
}
}
===== Presentation (Restcontroller) =====
Of note :
* Do not use DAO classes here!
* @PreAuthorize, adds a little security (role based here, still blind to ACL)
* @JsonView, \\
I recommend adding it default : "@JsonView(View.Public.class)" to every method which returns a ''model.db'' or packages a ''model.db''.\\
The benefit for you : you'll be able to choose the view used here : ''View.Public'' = all except for the fields marked with ''@JsonView'' extending ''View.Public''.
package be.mentoringsystems.merke.presentation.rest;
import be.mentoringsystems.merke.model.QueryParams;
import be.mentoringsystems.merke.model.db.Login;
import be.mentoringsystems.merke.model.dto.ApiDTO;
import be.mentoringsystems.merke.model.dto.LoginDTO;
import be.mentoringsystems.merke.model.dto.PagedListDTO;
import be.mentoringsystems.merke.model.dto.View;
import be.mentoringsystems.merke.service.LoginService;
import com.fasterxml.jackson.annotation.JsonView;
import java.util.UUID;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
/**
*
* @author anthonyarents
*/
@RestController
@RequestMapping("/logins")
public class LoginController {
@Autowired
private LoginService loginService;
@PreAuthorize("hasRole('ROLE_ADMIN')")
@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
@ResponseStatus(HttpStatus.OK)
public ApiDTO deleteById(@PathVariable UUID id) {
loginService.delete(id);
return new ApiDTO();
}
@JsonView(View.Login.class)
@RequestMapping(value = "/{id}", method = RequestMethod.PUT)
@ResponseStatus(HttpStatus.OK)
public ApiDTO updateById(@PathVariable UUID id, @RequestBody LoginDTO dto) {
dto.setId(id);
final Login result = loginService.save(dto);
return new ApiDTO(result);
}
@JsonView(View.Login.class)
@PreAuthorize("hasRole('ROLE_ADMIN')")
@RequestMapping(value = "", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
public ApiDTO createNew(@RequestBody LoginDTO dto) {
final Login result = loginService.save(dto);
return new ApiDTO(result);
}
@JsonView(View.Login.class)
@PreAuthorize("hasRole('ROLE_ADMIN')")
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ApiDTO getById(@PathVariable UUID id) {
final Login result = loginService.getById(id);
return new ApiDTO(result);
}
@JsonView(View.Login.class)
@PreAuthorize("hasRole('ROLE_ADMIN')")
@RequestMapping(value = "", method = RequestMethod.GET)
public PagedListDTO getAll(QueryParams queryParams) {
return loginService.getAllDTO(queryParams);
}
}
package be.mentoringsystems.merke.presentation.rest;
import be.mentoringsystems.merke.model.QueryParams;
import be.mentoringsystems.merke.model.db.Vendor;
import be.mentoringsystems.merke.model.dto.ApiDTO;
import be.mentoringsystems.merke.model.dto.PagedListDTO;
import be.mentoringsystems.merke.model.dto.VendorDTO;
import be.mentoringsystems.merke.model.dto.View;
import be.mentoringsystems.merke.service.VendorService;
import com.fasterxml.jackson.annotation.JsonView;
import java.util.UUID;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
/**
*
* @author anthonyarents
*/
@RestController
@RequestMapping("/vendors")
public class VendorController {
@Autowired
private transient VendorService vendorService;
@PreAuthorize("hasRole('ROLE_ADMIN')")
@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
@ResponseStatus(HttpStatus.OK)
public ApiDTO deleteById(@PathVariable UUID id) {
vendorService.delete(id);
return new ApiDTO();
}
@JsonView(View.Vendor.class)
@RequestMapping(value = "/{id}", method = RequestMethod.PUT)
@ResponseStatus(HttpStatus.OK)
public ApiDTO updateById(@PathVariable UUID id, @RequestBody VendorDTO dto) {
dto.setId(id);
final Vendor result = vendorService.save(dto);
return new ApiDTO(result);
}
@JsonView(View.Vendor.class)
@PreAuthorize("hasRole('ROLE_ADMIN')")
@RequestMapping(value = "", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
public ApiDTO createNew(@RequestBody VendorDTO dto) {
final Vendor result = vendorService.save(dto);
return new ApiDTO(result);
}
@JsonView(View.Vendor.class)
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ApiDTO getById(@PathVariable UUID id) {
final Vendor result = vendorService.getById(id);
return new ApiDTO(result);
}
@JsonView(View.Vendor.class)
@RequestMapping(value = "", method = RequestMethod.GET)
public PagedListDTO getAll(QueryParams queryParams) {
return vendorService.getAllDTO(queryParams);
}
}
===== Presentation (Advice) =====
InitBinder is stateful, this method is called for EVERY @RequestParam,
SimpleDateFormat which is not threadsafe won't be a problem here.
By setting a value for the InitBinder annotation, we can limit the usage to a specific variable.
package be.mentoringsystems.merke.presentation;
import be.mentoringsystems.merke.model.FilterMap;
import be.mentoringsystems.merke.model.SortingMap;
import be.mentoringsystems.merke.model.dto.ApiDTO;
import be.mentoringsystems.merke.presentation.conversion.FilterMapEditor;
import be.mentoringsystems.merke.presentation.conversion.SortingMapEditor;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.util.Throwables;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.WebRequest;
/**
*
* @author anthonyarents
*/
@ControllerAdvice(annotations = RestController.class)
public class Advice {
private static final Logger LOGGER = LogManager.getLogger(ControllerAdvice.class);
@InitBinder("queryParams")
public void initBinderForQueryParams(WebDataBinder binder) {
binder.registerCustomEditor(FilterMap.class, new FilterMapEditor());
binder.registerCustomEditor(SortingMap.class, new SortingMapEditor());
}
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd HH:mm"), true));
}
@ExceptionHandler(value = Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
public ApiDTO exception(Exception exception, WebRequest request) {
LOGGER.error(exception.getMessage(), exception);
return ApiDTO.error(Throwables.toStringList(exception));
}
}
===== Presentation (Conversion) =====
package be.mentoringsystems.merke.presentation.conversion;
import be.mentoringsystems.merke.model.Filter;
import be.mentoringsystems.merke.model.FilterMap;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import java.beans.PropertyEditorSupport;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
*
* @author anthonyarents
*/
public class FilterMapEditor extends PropertyEditorSupport {
private static final Logger LOGGER = LogManager.getLogger(FilterMapEditor.class);
@Override
public void setAsText(String text) throws IllegalArgumentException {
ObjectMapper mapper = new ObjectMapper();
final FilterMap filterMap = new FilterMap();
try {
List filters = mapper.readValue(text, TypeFactory.defaultInstance().constructCollectionType(ArrayList.class, Filter.class));
for(Filter filter : filters) {
filterMap.addFilter(filter);
}
} catch (IOException ex) {
LOGGER.error(ex.getMessage(), ex);
}
setValue(filterMap);
}
}
package be.mentoringsystems.merke.presentation.conversion;
import be.mentoringsystems.merke.model.Sorting;
import be.mentoringsystems.merke.model.SortingMap;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import java.beans.PropertyEditorSupport;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
*
* @author anthonyarents
*/
public class SortingMapEditor extends PropertyEditorSupport {
private static final Logger LOGGER = LogManager.getLogger(SortingMapEditor.class);
@Override
public void setAsText(String text) throws IllegalArgumentException {
ObjectMapper mapper = new ObjectMapper();
final SortingMap sortingMap = new SortingMap();
try {
List sorters = mapper.readValue(text, TypeFactory.defaultInstance().constructCollectionType(ArrayList.class, Sorting.class));
sortingMap.setSortings(sorters);
} catch (IOException ex) {
LOGGER.error(ex.getMessage(), ex);
}
setValue(sortingMap);
}
}
===== Server-side validation =====
At this time, I've only added a check to disallow "dangerous" html on fields which will display the html as is (instead of htmlEncoded like the rest of the output)
extra requirements on the project :
* el-api (v2.2 is used for tomcat6 & tomcat7, v3.0.0 is used with java8 & tomcat8)
* hibernate-validator (v5.4.1.Final in case of tomcat6 & 7, use latest in case of java8 & tomcat8)
* jsoup (use latest)
==== DTO ====
=== imports ===
import org.hibernate.validator.constraints.SafeHtml;
=== Getter ===
@SafeHtml
public String getDescription() {
return description;
}
==== Presentation ====
=== Imports ===
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
=== Changes to methods updateById & createNew ===
@JsonView(View.Public.class)
@PreAuthorize("hasRole('ROLE_ADMIN')")
@RequestMapping(value = "/{id}", method = RequestMethod.PUT)
@ResponseStatus(HttpStatus.OK)
public ApiDTO updateById(@PathVariable final UUID id, @RequestBody @Validated final NotificationDTO dto, final BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return ApiDTO.error();
}
dto.setId(id);
final Notification result = notificationService.save(dto);
return new ApiDTO(result);
}
@JsonView(View.Public.class)
@PreAuthorize("hasRole('ROLE_ADMIN')")
@RequestMapping(value = "", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
public ApiDTO createNew(@RequestBody @Validated final NotificationDTO dto, final BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return ApiDTO.error();
}
final Notification result = notificationService.save(dto);
return new ApiDTO(result);
}
===== Locale handling =====
==== Additional MvcConfiguration ====
@Bean
public LocaleResolver localeResolver(){
CustomLocaleResolver resolver = new CustomLocaleResolver();
resolver.setDefaultLocale(new Locale("nl"));
resolver.setCookieName("myLocaleCookie");
resolver.setCookieMaxAge(4800);
return resolver;
}
@Bean
public LocaleChangeInterceptor localeChangeInterceptor(){
LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor();
interceptor.setParamName("mylocale");
return interceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());
}
==== Locale resolver ====
package be.mentoringsystems.paypermail.presentation.locale;
import be.mentoringsystems.paypermail.model.db.Login;
import be.mentoringsystems.paypermail.service.LoginService;
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.i18n.CookieLocaleResolver;
/**
*
* @author anthonyarents
*/
public class CustomLocaleResolver extends CookieLocaleResolver {
@Autowired
private LoginService loginService;
@Override
public Locale resolveLocale(HttpServletRequest request) {
final Login login = loginService.getCurrentLogin();
if(login != null) {
final Locale locale = new Locale(login.getLanguage()); // can be any other language parameter on login.
request.setAttribute(LOCALE_REQUEST_ATTRIBUTE_NAME, locale);
request.setAttribute(TIME_ZONE_REQUEST_ATTRIBUTE_NAME, determineDefaultTimeZone(request));
return locale;
} else {
return super.resolveLocale(request);
}
}
}
===== Excel handling =====
==== ExportController ====
package be.mentoringsystems.merke.presentation;
import be.mentoringsystems.merke.model.QueryParams;
import be.mentoringsystems.merke.presentation.excel.VendorRequests;
import be.mentoringsystems.merke.presentation.excel.Vendors;
import be.mentoringsystems.merke.service.VendorRequestService;
import be.mentoringsystems.merke.service.VendorService;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
/**
*
* @author anthonyarents
*/
@RestController
@RequestMapping("/export")
public class ExportController {
@Autowired
private transient VendorRequestService vendorRequestService;
@Autowired
private transient VendorService vendorService;
@RequestMapping(value = "/vendorRequests", method = RequestMethod.POST)
public ModelAndView exportVendorRequests(QueryParams queryParams,
@RequestParam(name = "waitlist", defaultValue = "false", required = false) Boolean waitlist) {
// remove paging
queryParams.setLimit(0);
queryParams.setStart(0);
final Map objectMap = new HashMap();
objectMap.put("data", vendorRequestService.getAll(queryParams));
objectMap.put("waitlist", waitlist);
final VendorRequests view = new VendorRequests();
return new ModelAndView(view, objectMap);
}
@RequestMapping(value = "/vendors", method = RequestMethod.POST)
public ModelAndView exportVendors(QueryParams queryParams) {
// remove paging
queryParams.setLimit(0);
queryParams.setStart(0);
final Map objectMap = new HashMap();
objectMap.put("data", vendorService.getAll(queryParams));
final Vendors view = new Vendors();
return new ModelAndView(view, objectMap);
}
}
==== ExcelView ====
package be.mentoringsystems.merke.presentation.excel;
import be.mentoringsystems.merke.model.db.VendorRequest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.springframework.web.servlet.view.document.AbstractXlsxView;
/**
*
* @author anthonyarents
*/
public class VendorRequests extends AbstractXlsxView{
private void styledCell(Cell cell, String value, CellStyle style) {
cell.setCellValue(value);
cell.setCellStyle(style);
}
@Override
protected void buildExcelDocument(Map map, Workbook wrkbk, HttpServletRequest hsr, HttpServletResponse hsr1) throws Exception {
@SuppressWarnings("unchecked")
final List data = (List) map.get("data");
final Boolean waitlist = (Boolean) map.get("waitlist");
String documentName = "Aanvragen overzicht";
String documentTitle = "Export overzicht ";
if(Boolean.TRUE.equals(waitlist)) {
documentName = "Aanvragen wachtlijst";
documentTitle = "Export wachtlijst ";
}
final SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm");
hsr1.setHeader("Content-Disposition", "attachment; filename=\"" + documentName + ".xlsx\"");
final CellStyle acceptedStyle = wrkbk.createCellStyle();
acceptedStyle.setFillForegroundColor(IndexedColors.LIGHT_GREEN.getIndex());
acceptedStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
final CellStyle declinedStyle = wrkbk.createCellStyle();
declinedStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
declinedStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
final CellStyle headerStyle = wrkbk.createCellStyle();
headerStyle.setFillForegroundColor(IndexedColors.PALE_BLUE .getIndex());
headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
final Sheet sheet = wrkbk.createSheet();
final Row titlerow = sheet.createRow(0);
sheet.addMergedRegion(new CellRangeAddress(0,0,0,6));
titlerow.createCell(0).setCellValue(documentTitle + sdf.format(new Date()));
final Row headerrow = sheet.createRow(1);
styledCell(headerrow.createCell(0), "Wachtnr.", headerStyle);
styledCell(headerrow.createCell(1), "Voornaam", headerStyle);
styledCell(headerrow.createCell(2), "Naam", headerStyle);
styledCell(headerrow.createCell(3), "E-mailadres", headerStyle);
styledCell(headerrow.createCell(4), "Tel", headerStyle);
styledCell(headerrow.createCell(5), "Datum", headerStyle);
styledCell(headerrow.createCell(6), "Status", headerStyle);
int rowcounter = 0;
int rowoffset = 2;
for(VendorRequest request : data) {
final Row row = sheet.createRow(rowcounter + rowoffset);
rowcounter++;
row.createCell(0).setCellValue(rowcounter);
row.createCell(1).setCellValue(request.getVendor().getFirstname());
row.createCell(2).setCellValue(request.getVendor().getLastname());
row.createCell(3).setCellValue(request.getVendor().getEmail());
row.createCell(4).setCellValue(request.getVendor().getTelephonenumber());
row.createCell(5).setCellValue(sdf.format(request.getCreatedOn()));
if(Boolean.TRUE.equals(waitlist)) {
if(VendorRequest.WaitStatus.ACCEPTED.equals(request.getWaitstatus())) {
styledCell(row.createCell(6), "Goedgekeurd", acceptedStyle);
} else {
row.createCell(6).setCellValue("In afwachting");
}
} else {
switch (request.getStatus()) {
case ACCEPTED:
styledCell(row.createCell(6), "Goedgekeurd", acceptedStyle);
break;
case DECLINED:
styledCell(row.createCell(6), "Afgekeurd", declinedStyle);
break;
default:
row.createCell(6).setCellValue("In afwachting");
break;
}
}
}
sheet.autoSizeColumn(0, false);
sheet.autoSizeColumn(1, false);
sheet.autoSizeColumn(2, false);
sheet.autoSizeColumn(3, false);
sheet.autoSizeColumn(4, false);
sheet.autoSizeColumn(5, false);
sheet.autoSizeColumn(6, false);
}
}package be.mentoringsystems.merke.presentation.excel;
import be.mentoringsystems.merke.model.db.Category;
import be.mentoringsystems.merke.model.db.Vendor;
import be.mentoringsystems.merke.model.db.Vendorstall;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.springframework.web.servlet.view.document.AbstractXlsxView;
/**
*
* @author anthonyarents
*/
public class Vendors extends AbstractXlsxView{
private void styledCell(Cell cell, String value, CellStyle style) {
cell.setCellValue(value);
cell.setCellStyle(style);
}
@Override
protected void buildExcelDocument(Map map, Workbook wrkbk, HttpServletRequest hsr, HttpServletResponse hsr1) throws Exception {
@SuppressWarnings("unchecked")
final List data = (List) map.get("data");
String documentName = "Overzicht standhouders";
String documentTitle = "Export overzicht standhouders ";
final SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm");
hsr1.setHeader("Content-Disposition", "attachment; filename=\"" + documentName + ".xlsx\"");
final CellStyle headerStyle = wrkbk.createCellStyle();
headerStyle.setFillForegroundColor(IndexedColors.PALE_BLUE .getIndex());
headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
final Sheet sheet = wrkbk.createSheet();
final Row titlerow = sheet.createRow(0);
sheet.addMergedRegion(new CellRangeAddress(0,0,0,8));
titlerow.createCell(0).setCellValue(documentTitle + sdf.format(new Date()));
final Row headerrow = sheet.createRow(1);
styledCell(headerrow.createCell(0), "Nr.", headerStyle);
styledCell(headerrow.createCell(1), "Voornaam", headerStyle);
styledCell(headerrow.createCell(2), "Naam", headerStyle);
styledCell(headerrow.createCell(3), "Bedrijf", headerStyle);
styledCell(headerrow.createCell(4), "Tel", headerStyle);
styledCell(headerrow.createCell(5), "E-mailadres", headerStyle);
styledCell(headerrow.createCell(6), "Product", headerStyle);
styledCell(headerrow.createCell(7), "Ingeschreven sinds", headerStyle);
styledCell(headerrow.createCell(8), "Opmerking", headerStyle);
int rowcounter = 0;
int rowoffset = 2;
for(Vendor vendor : data) {
final Row row = sheet.createRow(rowcounter + rowoffset);
rowcounter++;
row.createCell(0).setCellValue(rowcounter);
row.createCell(1).setCellValue(vendor.getFirstname());
row.createCell(2).setCellValue(vendor.getLastname());
row.createCell(3).setCellValue(vendor.getCompanyName());
row.createCell(4).setCellValue(vendor.getTelephonenumber());
row.createCell(5).setCellValue(vendor.getEmail());
List categories = new ArrayList();
for(Vendorstall stall : vendor.getVendorstalls()) {
for(Category cat : stall.getCategories()) {
if(!categories.contains(cat)) {
categories.add(cat);
}
}
}
StringBuilder categoryString = new StringBuilder();
for(int i = 0; i < categories.size(); i++) {
if(i > 0) {
categoryString.append(", ");
}
categoryString.append(categories.get(i).getName());
}
row.createCell(6).setCellValue(categoryString.toString());
row.createCell(7).setCellValue(sdf.format(vendor.getCreatedOn()));
row.createCell(8).setCellValue("Nee");
}
sheet.autoSizeColumn(0, false);
sheet.autoSizeColumn(1, false);
sheet.autoSizeColumn(2, false);
sheet.autoSizeColumn(3, false);
sheet.autoSizeColumn(4, false);
sheet.autoSizeColumn(5, false);
sheet.autoSizeColumn(6, false);
sheet.autoSizeColumn(7, false);
sheet.autoSizeColumn(8, false);
}
}
==== ImportController ====
package be.mentoringsystems.merke.presentation;
import be.mentoringsystems.merke.importer.VendorImporter;
import java.util.UUID;
import javax.servlet.ServletContext;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
/**
*
* @author anthonyarents
*/
@RestController
@RequestMapping("/import")
public class ImportController {
@Value("#{contextParameters.uploadPath}")
private String uploadPath;
@Autowired
private ServletContext servletContext;
@Autowired
private VendorImporter vendorImporter;
private static final Logger LOGGER = LogManager.getLogger(ImportController.class);
@RequestMapping(value = "/vendors", method = RequestMethod.POST, consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}, produces = {MediaType.TEXT_PLAIN_VALUE, MediaType.TEXT_HTML_VALUE, MediaType.APPLICATION_JSON_VALUE})
public String importVendors(
@RequestPart(name = "file") MultipartFile file,
@RequestParam(name = "name") String name) throws Exception {
if (file != null && !file.isEmpty()) {
LOGGER.info("receiving file.");
UUID fileId = UUID.randomUUID();
StringBuilder uploadPathBuilder = new StringBuilder(uploadPath);
uploadPathBuilder.append("/").append(fileId.toString());
String mimeType = servletContext.getMimeType(file.getOriginalFilename());
String extension = FilenameUtils.getExtension(file.getOriginalFilename());
if (mimeType == null) {
// Set to binary type if MIME mapping isnot found.
mimeType = "application/octet-stream";
}
java.io.File serverFile = new java.io.File(uploadPathBuilder.toString() + "." + extension);
byte[] bytes = file.getBytes();
FileUtils.writeByteArrayToFile(serverFile, bytes);
LOGGER.info("starting import.");
vendorImporter.importFile(serverFile, fileId);
//fileService.saveFile(fileDTO);
LOGGER.info("File upload complete.");
return "{\"success\": true }";
} else {
throw new Exception("You failed to upload " + name + " because the file was empty.");
}
}
}
==== Importer Interface ====
package be.mentoringsystems.merke.importer;
import java.io.File;
import java.util.UUID;
/**
*
* @author anthonyarents
*/
public interface VendorImporter {
void importFile(final File file, final UUID fileId);
}
==== Importer Implementation ====
in the method ''private void mapRow(final Row row, final List columnHeaders, final UUID fileId)''\\
''values'' is used to import instead of ''result''. Usually importing would be done with the ''result'' map (since it has the column names).\\ In this case the format of imported files varied a lot.
package be.mentoringsystems.merke.importer.excel;
import be.mentoringsystems.merke.importer.VendorImporter;
import be.mentoringsystems.merke.model.db.Login;
import be.mentoringsystems.merke.model.db.Vendor;
import be.mentoringsystems.merke.model.db.Vendorstall;
import be.mentoringsystems.merke.service.LoginService;
import be.mentoringsystems.merke.service.VendorService;
import be.mentoringsystems.merke.service.VendorstallService;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.ss.util.CellReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
*
* @author anthonyarents
*/
@Component
public class VendorImporterImpl implements VendorImporter {
@Autowired
private transient VendorService vendorService;
@Autowired
private transient VendorstallService vendorstallService;
@Autowired
private transient LoginService loginService;
private static final Logger LOGGER = LogManager.getLogger(VendorImporterImpl.class);
@Override
public void importFile(File file, final UUID fileId) {
try {
//InputStream inp = new FileInputStream("workbook.xlsx");
final Workbook wb = WorkbookFactory.create(file);
final Sheet sheet = wb.getSheetAt(0); // only first sheet
final List columnHeaders = new ArrayList();
for (Row row : sheet) {
if (columnHeaders.isEmpty()) {
readColumnHeaders(row, columnHeaders);
} else {
mapRow(row, columnHeaders, fileId);
}
}
} catch (IOException ex) {
LOGGER.error(ex.getMessage(), ex);
} catch (InvalidFormatException ex) {
LOGGER.error(ex.getMessage(), ex);
} catch (EncryptedDocumentException ex) {
LOGGER.error(ex.getMessage(), ex);
}
}
@SuppressWarnings("deprecation")
private Object getCellContent(final Cell cell) {
// Alternatively, get the value and format it yourself
switch (cell.getCellTypeEnum()) {
case STRING:
return cell.getRichStringCellValue().getString();
case NUMERIC:
if (DateUtil.isCellDateFormatted(cell)) {
return cell.getDateCellValue();
} else {
return cell.getNumericCellValue();
}
case BOOLEAN:
return cell.getBooleanCellValue();
case FORMULA:
return cell.getCellFormula();
case BLANK:
return "";
default:
return null;
}
}
private String getAsString(final Object obj) {
if(obj == null) return null;
if(obj instanceof String) return (String) obj;
return obj.toString();
}
private void mapVendorAndVendorstall(final List columnHeaders, final List values, final UUID fileId) {
LOGGER.info("--- mapping to vendor and vendorstall");
final Vendor vendor = new Vendor();
final Vendorstall vendorstall = new Vendorstall();
final Login login = new Login();
vendor.setTitle((String) values.get(0));
vendor.setTitle2((String) values.get(1));
vendor.setFirstname((String) values.get(2));
vendor.setLastname((String) values.get(3));
vendor.setCompanyName((String) values.get(4));
vendor.setStreet((String) values.get(5));
Object zipcodeObj = values.get(6);
if(zipcodeObj instanceof String) {
vendor.setPostalcode((String) zipcodeObj);
} else {
Double zipcode = (Double) values.get(6);
vendor.setPostalcode(Integer.toString(zipcode.intValue()));
}
vendor.setCity((String) values.get(7));
vendor.setTelephonenumber((String) values.get(8));
vendor.setEmail((String) values.get(9));
vendor.setImportlistId(fileId);
vendorstall.setVendor(vendor);
StringBuilder unabletoimport = new StringBuilder();
unabletoimport.append(columnHeaders.get(10)).append(": ").append(values.get(10)).append(",\n"); // 10 : koopwaar
unabletoimport.append(columnHeaders.get(11)).append(": ").append(values.get(11)).append(",\n"); // 11 : koopwaar beschrijving
unabletoimport.append(columnHeaders.get(12)).append(": ").append(values.get(12)).append(",\n"); // 12 : koopwaar beschrijving cbs
unabletoimport.append(columnHeaders.get(13)).append(": ").append(values.get(13)).append(",\n"); // 13 : locatie (plaats op markt)
unabletoimport.append(columnHeaders.get(14)).append(": ").append(values.get(14)); // 14 : stand nr
vendorstall.setStalllength(getAsString(values.get(15)));
vendorstall.setStalldepth(getAsString(values.get(16)));
String vehicle = getAsString(values.get(17));
if(!StringUtils.isEmpty(vehicle) && !"-".equals(vehicle) && !"x".equals(vehicle)) {
vendorstall.setVehicle(true);
unabletoimport.append(",\n").append(columnHeaders.get(17)).append(": ").append(vehicle); // 17 : marktwagen
}
String disselordoor = getAsString(values.get(18));
if(!StringUtils.isEmpty(disselordoor) && !"-".equals(disselordoor) && !"x".equals(disselordoor)) {
vendorstall.setAdze(disselordoor.contains("dss:"));
vendorstall.setDoor(disselordoor.contains("dr:"));
if(vendorstall.isDoor()) {
vendorstall.setDoorlocation(disselordoor);
}
if(vendorstall.isAdze()) {
vendorstall.setAdzelocation(disselordoor);
}
}
String luifel = getAsString(values.get(19));
if(!StringUtils.isEmpty(luifel) && !"-".equals(luifel) && !"x".equals(luifel)) {
unabletoimport.append(",\n").append(columnHeaders.get(19)).append(": ").append(luifel); // 19 : luifel
}
String parasolortent = getAsString(values.get(20));
if(!StringUtils.isEmpty(parasolortent) && !"-".equals(parasolortent) && !"x".equals(parasolortent)) {
unabletoimport.append(",\n").append(columnHeaders.get(20)).append(": ").append(parasolortent); // 20 : parasol / tent
}
String wattage = getAsString(values.get(21));
if(!StringUtils.isEmpty(wattage) && !"-".equals(wattage) && !"x".equals(wattage)) {
vendorstall.setElectricity(true);
vendorstall.setElectricitypower(wattage);
}
vendor.setCompanyNumber(getAsString(values.get(22)));
vendor.setPermitNumber(getAsString(values.get(23)));
String favv = getAsString(values.get(24));
if(!StringUtils.isEmpty(favv) && !"-".equals(favv) && !"x".equals(favv)) {
unabletoimport.append(",\n").append(columnHeaders.get(24)).append(": ").append(favv); // 20 : parasol / tent
}
vendor.setIdnrNationalRegister((String) values.get(25));
if(StringUtils.isEmpty(vendor.getCompanyName())) {
vendor.setCompanyName((String) values.get(26));
}
vendor.setBankaccount(getAsString(values.get(27)));
// 28 : bedrag calculatie (formule)
String kolom1 = getAsString(values.get(29));
if(!StringUtils.isEmpty(kolom1) && !"-".equals(kolom1) && !"x".equals(kolom1)) {
unabletoimport.append(",\n").append(columnHeaders.get(29)).append(": ").append(kolom1); // 20 : parasol / tent
}
String kolom2 = getAsString(values.get(30));
if(!StringUtils.isEmpty(kolom2) && !"-".equals(kolom2) && !"x".equals(kolom2)) {
unabletoimport.append(",\n").append(columnHeaders.get(30)).append(": ").append(kolom2); // 20 : parasol / tent
}
String remarks = getAsString(values.get(31));
if(!StringUtils.isEmpty(remarks) && !"-".equals(remarks) && !"x".equals(remarks)) {
unabletoimport.append(",\n").append(columnHeaders.get(31)).append(": ").append(remarks); // 20 : parasol / tent
}
vendorstall.setVendor(vendor);
vendorstall.setExtra(unabletoimport.toString());
login.setFirstname(vendor.getFirstname());
login.setLastname(vendor.getLastname());
login.setUsername(vendor.getEmail());
if(StringUtils.isEmpty(login.getUsername())) {
final StringBuilder sb = new StringBuilder(login.getFirstname());
sb.append(".").append(login.getLastname());
login.setUsername(sb.toString());
}
login.setPassword("temp");
login.setActive(false);
login.setEmail(vendor.getEmail());
login.setGroup(2);
loginService.save(login);
vendor.setLoginId(login.getId());
vendorService.save(vendor);
vendorstallService.save(vendorstall);
LOGGER.info("--- done mapping to vendor and vendorstall");
}
private void mapRow(final Row row, final List columnHeaders, final UUID fileId) {
Map result = new HashMap();
final List values = new ArrayList();
LOGGER.info("--- reading row");
for (int cn = 0; cn < columnHeaders.size(); cn++) {
Cell c = row.getCell(cn, Row.MissingCellPolicy.RETURN_BLANK_AS_NULL);
CellReference cr = new CellReference(row.getRowNum(), cn);
if (c == null) {
// The spreadsheet is empty in this cell
values.add(null);
LOGGER.info("{}({}) does not have a value", columnHeaders.get(cn), cr.formatAsString());
} else {
final Object value = getCellContent(c);
result.put(columnHeaders.get(cn), value);
values.add(value);
LOGGER.info("{}({}) : {}", columnHeaders.get(cn), cr.formatAsString(), value);
}
}
LOGGER.info("--- done reading row");
if(values.get(0) == null) {
LOGGER.info("--- unable to map row");
} else {
mapVendorAndVendorstall(columnHeaders, values, fileId);
}
}
private void readColumnHeaders(Row row, List columnHeaders) {
for (Cell cell : row) {
columnHeaders.add(cell.getStringCellValue());
}
}
}
====== Log4J2 config ======
Add ''log4j2.json'' to ''/WEB-INF/'' \\
{
"configuration": {
"status": "info",
"appenders": {
"Console": {
"name": "STDOUT",
"PatternLayout": {
"pattern": "%d %-5p [%c{1}] %m %n"
}
}
},
"ThresholdFilter": {
"level": "info"
},
"loggers": {
"root": {
"level": "info",
"additivity": "false",
"AppenderRef": {
"ref": "STDOUT"
}
}
}
}
}
====== JSP Pages ======
===== Login =====
<%@page contentType="text/html" pageEncoding="UTF-8"%>
BoardPortal
Aanmelden
===== App =====
Add ''index.jsp'' to ''/WEB-INF/jsp'' \\
this should be the index.html of your extjs application (slighly modified like :)
BoardPortal
====== API ======
Our api is now ready for extjs6, in order to use the filtering, you'll have to modify a store to "remoteFilter: true".
====== Static resources (javascript, css, ...) ======
Don'y use html pages, use jsp like above.
simply place the resource in ''/static/'' or any subfolder there
====== EXT JS 6 ======
===== A new App =====
==== Setup ====
- Download and Install Sencha Cmd 6
- Download and unzip the Ext JS 6 SDK (remember the path)
==== To start a new EXTJS project ====
- Open your terminal or console window and issue these commands *inside* the static folder :
sencha -sdk /path/to/ext6 generate app
cd
sencha app watch
This will generate a skeleton app, ''sencha app watch'' will check for any changes made to css or js.
==== To take over an existing EXTJS project ====
- Open your terminal or console window and issue these commands *inside* the app folder :
sencha app upgrade /path/to/sdk/extjs
sencha app watch
This will allow you to work with the ext project, ''sencha app watch'' will check for any changes made to css or js.
==== Locale package for modern ====
download {{:java:locale.zip|this}} & place it in ''static/extjsproject/ext/modern/'' with the name locale
==== Configuration to be aware of ====
==== Time to start changing the application ====
You can now start editting your application.
Desktop code usually goes into ''/classic/src'', stylechanges in ''/classic/sass''\\
Mobile/Tablet code usually goes into ''/modern/src'', stylechanges in ''/modern/sass''
==== Notes ====
* You can only use "classic" extjs classes in the classic folder, & "modern" (touch) classes in the modern folder.
* Binding doesn't always work like it's supposed to, but it makes the code really neat and you should try it out.
* Model relations don't always work like they're supposed to, try to avoid them
* if a ui config doesn't work, it's likely that the name was already taken
===== Adjusting your build ====
**static/BoardPortal/app.json :**
find the mentioning of **BUILDCHANGE** in the comments to differentiate between a production build & a development build
===== build for testing =====
* change app.json to use index.html (as mentionned in BUILDCHANGE comments)
* go to static/BoardPortal with terminal/cmd
* use "sencha app build classic"
* use "sencha app watch" to deal with all javascript changes
===== build for production =====
* change app.json to use ../index.html (as mentionned in BUILDCHANGE comments)
* go to static/BoardPortal with terminal/cmd
* use "sencha app build classic"
====== Java ======
Install a good JDK on your server. for Ubuntu 14, I recommend forcing java 8 on it :
http://ubuntuhandbook.org/index.php/2015/01/install-openjdk-8-ubuntu-14-04-12-04-lts/
''sudo add-apt-repository ppa:openjdk-r/ppa'' \\
''sudo apt-get update'' \\
''sudo apt-get install openjdk-8-jdk''\\
Java 8 makes it a bit easier to manage (they removed permsize)
====== Tomcat 7 ======
https://www.digitalocean.com/community/tutorials/how-to-install-apache-tomcat-7-on-ubuntu-14-04-via-apt-get
===== install =====
''sudo apt-get install tomcat7'' \\
''sudo apt-get install tomcat7-user'' \\
===== create an instance =====
create a path like ''/var/tomcat7/servers/''\\
''cd /var/tomcat7/servers/''\\
change ports (8080=http port, 8005=control/shutdown port)\\
''sudo tomcat7-instance-create -p 8080 -c 8005 ''\\
''sudo chown -R tomcat7: ''\\
activate AJP connector + change it's port (default 8009) in\\
''/conf/server.xml''
====== Certificates ======
[[https://letsencrypt.org/]] free certificate provider (certificate is 90 days with auto renewal)\\
[[https://certbot.eff.org/]] easy AUTOMATIC install \\
\\
the certbot-auto script is usually placed in ''/usr/share/'' (as seen in crontab)\\
==== Quick way to add domains ====
you can simply boot the script :\\
''cd /usr/share/''\\
''sudo ./certbot-auto''\\
the interface will then present you with the options you can provide a certificate for, it's 1 certificate for all urls you select.
===== provide for normal domains =====
''sudo ./certbot-auto --apache''
===== update existing certificate by adding www =====
''sudo ./certbot-auto --apache -d cliclapaybymail.be -d www.cliclapaybymail.be''
====== Boardportal ======
server path : /var/lib/tomcat7/\\
webapps folder : /var/lib/tomcat7/webapps/\\
stop : sudo -u tomcat7 /var/lib/tomcat7/bin/shutdown.sh\\
start : sudo -u tomcat7 /var/lib/tomcat7/bin/startup.sh\\
context.xml is now ALWAYS located under /var/lib/tomcat7/webapps/BoardPortal/META-INF/context.xml
PS: don't forget to copy context.xml before changing the webapp. You'll lose database settings !