Skip to content
Snippets Groups Projects
Commit e6bb9986 authored by Martin Ledvinka's avatar Martin Ledvinka
Browse files

Merge branch 'master' of gitlab.fel.cvut.cz:ear/setup-project

parents f3efd27f b4912bfd
Branches master
No related tags found
No related merge requests found
Showing
with 238 additions and 20 deletions
......@@ -4,6 +4,12 @@ This project is a sample for verifying correct system development setup.
If it can be built and run, the system has the correct setup and is ready for the B6B33EAR course.
This file contains:
1. A list of libraries and platforms necessary for developing applications in the course,
2. Guide for running this project,
3. Description of some common problems and their solutions,
4. Description of the structure of the application.
### Software Requirements
* **Java 8** (download [here](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html))
......@@ -66,3 +72,17 @@ Or, you can tell Windows to support longer paths - see [https://msdn.microsoft.c
* If `npm install` fails with errors on _Unmet peer dependencies_, make sure you are running npm at least version 3, which no longer treats missing peer dependencies as error.
* Make sure sufficient rights are set both on the project directory and on the directory of the Tomcat server, which is used to deploy your application.
* Make sure there are no spaces in paths to both your project and the Tomcat. Especially on Windows, where Tomcat is often installed into `Program Files`.
### Application Structure
The application's backend contains the following packages:
* `config` package contains configuration of the application, mainly Spring configuration,
* `dao` contains Data access objects,
* `model` contains our entity classes,
* `rest` contains REST web services,
* `service` contains Spring services - business logic belongs here. In case the business logic consists of service interfaces
and separate implementations, it will probably require separation of the implementations into a subpackage.
Of course, the package naming and structure is up to the developer.
......@@ -4,20 +4,28 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- Group can contain any number of artifacts -->
<groupId>cz.cvut.kbss</groupId>
<!-- ArtifactId identifies the project in the group -->
<artifactId>ear-setup</artifactId>
<version>0.0.1</version>
<version>0.0.2</version>
<!-- war packaging is important, otherwise Maven would try to package the application into a jar -->
<packaging>war</packaging>
<properties>
<!-- Tell maven that we are using UTF-8 file encoding -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- Tell maven we are using Java 8, by default it assumes Java 5, which is reaally old -->
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<!-- Declare versions as properties, so we can easily change them -->
<org.springframework.version>4.3.2.RELEASE</org.springframework.version>
<org.springframework.security.version>4.1.1.RELEASE</org.springframework.security.version>
<com.fasterxml.jackson.version>2.7.3</com.fasterxml.jackson.version>
<org.postgresql.version>9.4.1209</org.postgresql.version>
<ch.qos.logback.version>1.1.6</ch.qos.logback.version>
<junit.version>4.12</junit.version>
</properties>
<dependencies>
......@@ -70,19 +78,36 @@
<version>${org.springframework.version}</version>
</dependency>
<!-- Jackson for JSON serialization -->
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${org.springframework.security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${org.springframework.security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${org.springframework.security.version}</version>
</dependency>
<!-- Jackson for JSON (de)serialization -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${com.fasterxml.jackson.version}</version>
</dependency>
<!-- Servlet-API -->
<!-- Servlet-API. Necessary for web applications -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
<scope>provided</scope> <!-- provided means that the application server provides the library -->
</dependency>
<!-- PostgreSQL driver -->
......@@ -92,7 +117,15 @@
<version>${org.postgresql.version}</version>
</dependency>
<!-- Connection pool -->
<!-- H2 database for tests (it includes a JDBC driver) -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.190</version>
<scope>test</scope>
</dependency>
<!-- Database connection pool -->
<dependency>
<groupId>com.jolbox</groupId>
<artifactId>bonecp</artifactId>
......@@ -115,22 +148,48 @@
<artifactId>logback-classic</artifactId>
<version>${ch.qos.logback.version}</version>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<!-- Spring support for tests -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework.version}</version>
<scope>test</scope>
</dependency>
<!-- Mockito for mocks and test spies -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Compiler plugin takes care of compilation of the source codes -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
</plugin>
<!-- War plugin packages the application into a deployable war archive -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.4</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
<!-- Name of the produced .war file (by default, it is $artifactId-$version.war) -->
<warName>ear-setup</warName>
<!-- Exclude files and folders which are not necessary in the war -->
<warSourceExcludes>
node_modules/,package.json,.babelrc,.eslintrc
</warSourceExcludes>
......@@ -162,5 +221,4 @@
<scm>
<connection>scm:git:https://gitlab.fel.cvut.cz/ear/setup-project.git</connection>
</scm>
</project>
\ No newline at end of file
</project>
/**
* Copyright (C) 2016 Czech Technical University in Prague
*
* <p>
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
......@@ -19,9 +19,16 @@ import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.EnableMBeanExport;
import org.springframework.context.annotation.Import;
/**
* Root configuration file of our project - it sets up basic Spring configuration and imports additional configuration files.
* <p>
* It is good to separate configuration of different components of the application, because they can then be configured
* independently for example in tests.
*/
// This annotation is required when services without separate interfaces are used. It causes cglib-based proxies of
// the services to be used - see http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/EnableAspectJAutoProxy.html#proxyTargetClass--
@EnableAspectJAutoProxy(proxyTargetClass = true)
@Configuration
@EnableMBeanExport
@Import({WebAppConfig.class, PersistenceConfig.class, ServiceConfig.class})
@Configuration // This class is a Spring configuration
@Import({WebAppConfig.class, PersistenceConfig.class, ServiceConfig.class}) // Import additional configuration classes
public class AppConfig {
}
......@@ -20,8 +20,16 @@ import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatche
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
/**
* This class is called when our project is deployed into an application server - the servers have hooks for such cases.
* <p>
* It initializes Spring context and starts building beans according to our configuration
*/
public class DispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* Notice how we are referencing the {@link AppConfig} class, which is basically the root of our application's configuration.
*/
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[]{AppConfig.class};
......
......@@ -27,11 +27,15 @@ import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.util.Properties;
/**
* Set up our persistence - access to data source (the database) and JPA EMF. We are also telling Spring where to find
* configuration files for the persistence setup.
*/
@Configuration
@PropertySources(
{@PropertySource("classpath:jpa.properties"),
@PropertySource("classpath:jdbc.properties")})
@EnableTransactionManagement
@EnableTransactionManagement // Enable use of the @Transactional annotation
@ComponentScan(basePackages = "cz.cvut.kbss.ear.setup.dao")
public class PersistenceConfig {
......
/**
* Copyright (C) 2016 Czech Technical University in Prague
*
* <p>
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
......@@ -25,6 +25,11 @@ import org.springframework.context.annotation.Configuration;
@ComponentScan(basePackages = "cz.cvut.kbss.ear.setup.rest")
public class RestConfig {
/**
* Object mapper is used to serialize POJOs into JSON and vice versa. It is a class from the Jackson framework.
* <p>
* We are creating it here so that we can configure it a little.
*/
@Bean
public ObjectMapper objectMapper() {
final ObjectMapper objectMapper = new ObjectMapper();
......
......@@ -23,6 +23,10 @@ import org.springframework.web.client.RestTemplate;
@ComponentScan(basePackages = "cz.cvut.kbss.ear.setup.service")
public class ServiceConfig {
/**
* {@link RestTemplate} can be used to communicate with web services of another application -
* see for example <a href="http://www.baeldung.com/rest-template">http://www.baeldung.com/rest-template</a>.
*/
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
......
......@@ -28,6 +28,12 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter
import java.nio.charset.Charset;
import java.util.List;
/**
* This class configures the web services of our application, it tells spring where to find static resources like JS files, so that
* it can optimize access to them (e.g. skip authentication) and how to handle coversion of JSON to Java and vice versa.
* <p>
* This setup is usually done once and then copied into new projects.
*/
@Configuration
@EnableWebMvc
@Import({RestConfig.class})
......
......@@ -4,7 +4,6 @@ import cz.cvut.kbss.ear.setup.model.Teacher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
......@@ -20,12 +19,10 @@ public class TeacherDao {
@PersistenceContext
private EntityManager em;
@Transactional(readOnly = true)
public List<Teacher> findAll() {
return em.createNamedQuery("Teacher.findAll", Teacher.class).getResultList();
}
@Transactional(readOnly = true)
public Teacher findByName(String firstName, String lastName) {
Objects.requireNonNull(firstName);
Objects.requireNonNull(lastName);
......@@ -37,7 +34,6 @@ public class TeacherDao {
}
}
@Transactional
public void persist(Teacher teacher) {
Objects.requireNonNull(teacher);
em.persist(teacher);
......
......@@ -4,6 +4,7 @@ import cz.cvut.kbss.ear.setup.dao.TeacherDao;
import cz.cvut.kbss.ear.setup.model.Teacher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
......@@ -13,14 +14,22 @@ public class TeacherService {
@Autowired
private TeacherDao teacherDao;
// Declarative transaction demarcation. readOnly set to true is good for retrieval operations, because it
// may involve less locking on the lower layers
// If the services are split into interfaces and implementations, the @Transactional annotation usually goes
// to the interface, so that ic can apply to all possible implementations of the service
@Transactional(readOnly = true)
public List<Teacher> findAll() {
return teacherDao.findAll();
}
@Transactional(readOnly = true)
public boolean exists(String firstName, String lastName) {
return teacherDao.findByName(firstName, lastName) != null;
}
// No readOnly here, because we modify the data in this operation
@Transactional
public void persist(Teacher teacher) {
teacherDao.persist(teacher);
}
......
......@@ -19,7 +19,7 @@
<appender-ref ref="STDOUT"/>
</logger>
<!-- By default, the level of the root level is set to INFO -->
<!-- By default, the level of the root logger is set to INFO -->
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
......
/**
* Copyright (C) 2016 Czech Technical University in Prague
* <p>
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any
* later version.
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details. You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package cz.cvut.kbss.ear.setup.dao;
import cz.cvut.kbss.ear.setup.config.PersistenceConfig;
import org.junit.runner.RunWith;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
/**
* This class configures our tests so that we can use the Spring features in them - e.g. autowiring.
* <p>
* It is often good to extract this setup into a common superclass, so that we need not set the configuration on every test class.
*/
@RunWith(SpringJUnit4ClassRunner.class) // Tell JUnit to use Spring's test runner
/*
Which configuration classes should Spring load. This also means that for example service classes and REST controllers
won't be available for autowiring in tests inheriting from this class.
*/
@ContextConfiguration(classes = {PersistenceConfig.class})
// Reset the Spring context after each tests, recreating all the beans
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
//extend the transactions to whole tests in order to rollback the changes after each test
// Se also http://docs.spring.io/spring/docs/current/spring-framework-reference/html/integration-testing.html#testcontext-tx
@Transactional(transactionManager = "txManager")
public abstract class BaseDaoTestRunner {
}
package cz.cvut.kbss.ear.setup.dao;
import cz.cvut.kbss.ear.setup.model.Teacher;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import static org.junit.Assert.*;
public class TeacherDaoTest extends BaseDaoTestRunner {
// We can use DI thanks to the Spring test container
@Autowired
private TeacherDao teacherDao;
@Test
public void findByNameFindsTeacherByFirstNameAndLastName() {
final Teacher t = new Teacher();
t.setFirstName("Severus");
t.setLastName("Snape");
t.setEmail("severus.snape@hogwarts.co.uk");
t.setRoom("E-227");
teacherDao.persist(t);
final Teacher result = teacherDao.findByName(t.getFirstName(), t.getLastName());
assertNotNull(result);
assertEquals(t.getId(), result.getId());
}
}
\ No newline at end of file
jdbc.driverClassName=org.h2.Driver
jdbc.url=jdbc:h2:mem:ear
jdbc.username=ear
jdbc.password=ear
jpa.platform=org.eclipse.persistence.platform.database.H2Platform
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- Send debug messages to System.out -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- By default, encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
<encoder>
<pattern>%date{dd-MM-yyyy HH:mm:ss.SSS} [%thread] %-5level %logger{30} - %msg%n</pattern>
</encoder>
</appender>
<!-- Logger for our app -->
<logger name="cz.cvut.kbss" level="TRACE" additivity="false">
<appender-ref ref="STDOUT"/>
</logger>
<!-- Restrict logging of Spring -->
<logger name="org.springframework" level="INFO" additivity="false">
<appender-ref ref="STDOUT"/>
</logger>
<!-- By default, the level of the root level is set to DEBUG -->
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment