Java source

The important parts of the Java source code are as follows:

  • StoreApp.java: This is the main entry class for the application. Since this is a Spring Boot application, the main class is executable and you can start the application by running this class from an IDE. Let's take a look at this class:
    • The class is annotated with a bunch of Spring JavaConfig annotations:
@SpringBootApplication
@EnableConfigurationProperties({LiquibaseProperties.class, ApplicationProperties.class})
    • The first one, @SpringBootApplication, allows the required configurations for Spring to scan the source files and auto-detects Spring components (services, repository, resource, configuration classes that define Spring beans, and so on). It also tries to guess and auto-configure beans that the application might need based on classes that are found on the classpath and the configurations we have provided.
    • The second one, @EnableConfigurationProperties, helps us register additional configurations for the application via property files.
    • The main method of the class bootstraps the Spring Boot application and runs it:
    public static void main(String[] args) {
SpringApplication app = new
SpringApplication(StoreApp.class)
;
DefaultProfileUtil.addDefaultProfile(app);
Environment env = app.run(args).getEnvironment();
logApplicationStartup(env);
}
  • config: This package contains Spring bean configurations for the database, cache, WebSocket, and so on. This is where we will configure various options for the application. Some important ones include:
  • CacheConfiguration.java: This class configures the Hazelcast cache for the application. 
  • DatabaseConfiguration.java: This class configures the database for the application and enables transaction management, JPA auditing, and JPA repositories for application.SecurityConfiguration.java: This is a very important part of the application as it configures security for it. Let's take a look at the important parts of the class:
  • Following annotations enable web security and method-level security so that we can use the @Secured and @Pre/PostAuthorize annotations on individual methods:
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true,
securedEnabled
= true)
  • The following configuration tells the application to ignore static content and certain APIs from the Spring Security configuration:
    public void configure(WebSecurity web) {
web.ignoring()
.antMatchers(HttpMethod.OPTIONS,
"/**")
.antMatchers("/app/*/
.{js,html}")
.antMatchers("/i18n/**")
.antMatchers("/content/**")
.antMatchers("/h2-console/**")
.antMatchers("/swagger-ui
/index.html")
.antMatchers("/test/**");
}
  • The following configuration tells Spring Security which endpoints are permitted for all users, which endpoints should be authenticated, and which endpoints require a specific role (ADMIN, in this case):
public void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.csrf()
.disable()
.addFilterBefore(corsFilter,
UsernamePasswordAuthentication
Filter.class)
.exceptionHandling()
.authenticationEntryPoint
(problemSupport)
.accessDeniedHandler(problem
Support)
.and()
.headers()
.contentSecurityPolicy("...")
.and()
.referrerPolicy(...)
.and()
.featurePolicy("...")
.and()
.frameOptions()
.deny()
.and()
.sessionManagement()
.sessionCreationPolicy(Session
CreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/authenticate")
.permitAll()
.antMatchers("/api/register").
permitAll()
.antMatchers("/api/activate").
permitAll()
.antMatchers("/api/account/reset-
password/init").permitAll()
.antMatchers("/api/account/reset-
password/finish").permitAll()
.antMatchers("/api/**").
authenticated()
.antMatchers("/websocket/tracker")
.hasAuthority(Authorities
Constants.ADMIN)
.antMatchers("/websocket/**").
permitAll()
.antMatchers("/management
/health").permitAll()
.antMatchers("/management
/info").permitAll()
.antMatchers("/management
/prometheus").permitAll()
.antMatchers("/management/**").
hasAuthority(Authorities
Constants.ADMIN)
.and()
.httpBasic()
.and()
.apply(securityConfigurer
Adapter());
// @formatter:on
}
  • WebConfigurer.java: This is where we set up HTTP cache headers, MIME mappings, static asset locations, and Cross-Origin Resource Sharing (CORS).
JHipster provides great CORS support out of the box:
  • CORS can be configured using the jhipster.cors property, as defined in the JHipster common application properties (http://www.jhipster.tech/common-application-properties/).
  • CORS support is enabled by default in dev mode for monoliths and gateways. It is disabled by default for microservices as you are supposed to access them through a gateway.
  • CORS support is disabled by default in prod mode for both monoliths and microservices, for security reasons.
  • domain: Domain model classes for the application are in this package. These are simple POJOs (short for Plain Old Java Objects) which have JPA annotations mapping it to a Hibernate entity. When the Elasticsearch option is selected, these also act as Document objects. Let's take a look at the User.java class:
    • An entity class is characterized by the following annotations. The @Entity annotation marks the class as a JPA entity. The @Table annotation maps the entity to a database table. The @Cache annotation enables second-level caching of the entity, and it also specifies a caching strategy:
@Entity
@Table(name = "jhi_user")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
  • There are various annotations that are used at the field level in these classes. @Id marks the primary key for the entity. @Column maps a field to a database table column by the same name when no override is provided. @NotNull, @Pattern, and @Size are annotations that are used for validation. @JsonIgnore is used by Jackson to ignore fields when converting objects that are to be returned in REST API requests into JSON. This is especially useful with Hibernate as it avoids circular references between relationships, which create tons of SQL DB requests and failures:
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@NotNull
@Pattern(regexp = Constants.LOGIN_REGEX)
@Size(min = 1, max = 50)
@Column(length = 50, unique = true, nullable = false)
private String login;

@JsonIgnore
@NotNull
@Size(min = 60, max = 60)
@Column(name = "password_hash", length = 60, nullable
= false)
private String password;
    • Relationships between database tables are also mapped to entities using JPA annotations. In the following, for example, it maps a many-to-many relationship between a user and user authorities. It also specifies a join table to be used for the mapping:
@JsonIgnore
@ManyToMany
@JoinTable(
name = "jhi_user_authority",
joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")},
inverseJoinColumns = {@JoinColumn(name = "authority_name", referencedColumnName = "name")})
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@BatchSize(size = 20)
private Set<Authority> authorities = new HashSet<>();

  • repository: This package holds Spring Data repositories for entities. These are typically interface definitions that are automatically implemented by Spring Data. This removes the need for us to write any boilerplate implementations for the data access layer. Let's look at the UserRepository.java example:
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
...

Optional<User> findOneByActivationKey(String activationKey);

List<User> findAllByActivatedIsFalseAndActivationKey
IsNotNullAndCreatedDateBefore(Instant dateTime);

Optional<User> findOneByResetKey(String resetKey);

Optional<User> findOneByEmailIgnoreCase(String email);

Optional<User> findOneByLogin(String login);

@EntityGraph(attributePaths = "authorities")
Optional<User> findOneWithAuthoritiesById(Long id);

@EntityGraph(attributePaths = "authorities")
@Cacheable(cacheNames = USERS_BY_LOGIN_CACHE)
Optional<User> findOneWithAuthoritiesByLogin(String login);

@EntityGraph(attributePaths = "authorities")
@Cacheable(cacheNames = USERS_BY_EMAIL_CACHE)
Optional<User> findOneWithAuthoritiesByEmail(String email);

Page<User> findAllByLoginNot(Pageable pageable, String login);
}

From the preceding code block, we can notice the following:

    • The @Repository annotation marks this as a Spring Data repository component.
    • The interface extends JpaRepository, which lets it inherit all default CRUD operations, such as findOne, findAll, save, count, and delete.
    • Custom methods are written as simple method definitions that follow Spring Data naming conventions so that the method name specifies the query to be generated. For example, findOneByEmailIgnoreCase generates a query equivalent of SELECT * FROM user WHERE LOWER(email) = LOWER(:email).
  • security: This package holds Spring Security-related components and utilities. Since we chose JWT as our authentication mechanism, it holds JWT-related classes such as TokenProvider, JWTFilter, and JWTConfigurer as well.
  • service: This package holds the service layer, which consists of Spring service beans, DTOs, MapStruct DTO mappers, and service utilities.
  • web: This package holds web resource classes, view models classes, and utility classes:
    • rest: This package holds Spring resource classes for the REST API. It also holds view model objects and utilities. Let's take a look at UserResource.java:
      • Resource classes are marked with the @RestController and @RequestMapping("/api") annotations from Spring. The latter specifies the base URL path for the controller so that all <applicationContext>/api/* requests are forwarded to this class.
      • Request methods are annotated with annotations according to their purpose. For example, the following marks the createUser method as a PostMapping for "/users", which means all POST requests to <applicationContext>/api/users will be served by this method. The @PreAuthorize annotation restricts the method's access to the specified role:
@PostMapping("/users")
@PreAuthorize("hasRole("" +
AuthoritiesConstants.ADMIN + "")")
public ResponseEntity<User> createUser(@Valid
@RequestBody UserDTO userDTO) throws
URISyntaxException {
...
}
    • WebSocket: This package holds WebSocket controllers and view models.
JHipster uses Data Transfer Objects (DTOs) and View Models (VMs) on the server-side. DTOs are used to transfer data from the service layer to and from the resource layer. They break Hibernate transactions and avoid further lazy loading from being triggered by the resource layer since all the mapping is done without any references to the entity objects being held. VMs are only used to display data on the web frontend and don't interact with the service layer.

We'll look at configurations that are used for the application in the next section.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.80.122