Java source

The important parts of the Java source code are:

@ComponentScan
@EnableAutoConfiguration(exclude = {MetricFilterAutoConfiguration.class, MetricRepositoryAutoConfiguration.class})
@EnableConfigurationProperties({LiquibaseProperties.class, ApplicationProperties.class})
public static void main(String[] args) throws UnknownHostException {
SpringApplication app = new SpringApplication(StoreApp.class);
DefaultProfileUtil.addDefaultProfile(app);
Environment env = app.run(args).getEnvironment();
...
}
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers(HttpMethod.OPTIONS, "/**")
.antMatchers("/app/**/*.{js,html}")
.antMatchers("/i18n/**")
.antMatchers("/content/**")
.antMatchers("/swagger-ui/index.html")
.antMatchers("/api/register")
.antMatchers("/api/activate")
.antMatchers("/api/account/reset-
password/init"
)
.antMatchers("/api/account/reset-
password/finish"
)
.antMatchers("/test/**")
.antMatchers("/h2-console/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
...
.and()
.authorizeRequests()
.antMatchers("/api/register").permitAll()
...
.antMatchers("/api/**").authenticated()
.antMatchers("/websocket/tracker")
.hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/websocket/**").permitAll()
.antMatchers("/management/health").permitAll()
.antMatchers("/management/**")
.hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/v2/api-docs/**").permitAll()
.antMatchers("/swagger-resources/configuration/ui").permitAll()
.antMatchers("/swagger-ui/index.html")
.hasAuthority(AuthoritiesConstants.ADMIN)
.and()
.apply(securityConfigurerAdapter());
}
  • domain: The domain model classes for the application are in this package. These are simple POJOs which have JPA annotations mapping it to a Hibernate entity. When the Elasticsearch option is selected, these also act as the Document object. 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 used at 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 the objects into JSON which are to be returned in the REST API requests. This is especially useful with Hibernate as it avoids circular references between relationships, which create tons of SQL DB requests and fail:
@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)
private String password;
    • The relationships between the database tables are also mapped to the entities using JPA annotations. Here, 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 the Spring Data repositories for the entities. These typically interface definitions which 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> findAllByActivatedIsFalseAndCreatedDateBefore(Instant
dateTime);

Optional<User> findOneByResetKey(String resetKey);

Optional<User> findOneByEmailIgnoreCase(String email);
...
}
    • The  @Repository annotation marks this as a Spring data repository component.
    • The interface extends JpaRepository, which lets it inherit all the default CRUD operations like findOne, findAll, save, count, and delete.
    • Custom methods are written as simple method definitions following the 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 utils, and 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 consisting of Spring service beans, DTOs, Mapstruct DTO mappers, and service utilities.
  • web: This package holds the 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:
      • The 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 below 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 @Timed annotation is used to measure the performance of the method. The @Secured annotation restricts the method access to the specified role:
@PostMapping("/users")
@Timed
@Secured(AuthoritiesConstants.ADMIN)
public ResponseEntity createUser(@Valid @RequestBody ManagedUserVM managedUserVM) throws URISyntaxException {
...
}
    • WebSocket: This package holds the Websocket controllers and view models.
JHipster uses DTO (Data Transfer Object) and VM (View Model) on the server side. DTOs are for transferring data from the service layer to and from the resource layer. They break the Hibernate transactions and avoids further lazy loading from being triggered by the resource layer. VMs are only used for displaying data on the web frontend and don't interact with the service layer.