Angular2 & Spring boot en mode RESTful

A installer Mysql, Spring Tool Suite 3, Java 1.8 minimun, Visual Studio Code 14 avec plugin Debugger fro chrome, le navigateur chrome , node.js 12

 

Etape 1/ utilisation de Spring Initializr

Allez sur https://start.spring.io/ pour fabriquer le fichier de configuration des jars compatibles pour réaliser nos actions.

Spring Start pour configuration automatique

Télécharger le projet au format zip et le placer dans le workspace

Importer le projet dans l’outil Spring Tool Suite™ 3 avec une version d’eclipse spécialement configuré pour Spring.

 

 

 

Etape 2/ Utilisation de Maven

Le projet est importé, exécution sur le projet Maven > Run > Maven install :

Mais erreur de configuration du JDK dans la console:

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project todo: Compilation failure
[ERROR] No compiler is provided in this environment. Perhaps you are running on a JRE rather than a JDK?

Ajout donc du JDK pour le projet, clic droit sur le Projet > Build Path > Configure Build Path >

Puis Onglet Library : choisir le JRE System puis bouton « Edit … »

Puis dans la nouvelle fenêtre choisir « Installed JRE »

Dans la nouvelle fenêtre faire « Add… », dans la nouvelle fenêtre faire « Standard VM » puis « Next » et indiquer le chemin du JDK sur votre ordinateur dans le champ « JRE Home » puis si le JDK indiqué est valide alors le bouton « Finish » ramène à la fenêtre précédente…

Validez le JDK installé par défaut en cochant la case à gauche comme sur cette copie d’écran:

Fermer les fenêtres avec « Apply and close » puis vérifier que le « bon » JDK est indiqué sur cette dernière fenêtre avant de faire « Finish »:

Eclipse maven run maven install

Relancer mais maintenant problème du JPA et de la base de données:

Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.

Si le déploiement de Spring ne fonctionne pas, il faut vérigier que le fichier application.properties existe bien. Sinon le copier coller depuis l’étape 5

 

 

Etape 3/ Fabrication d’une classe Todo entity et configuration BD

Faire une classe Todo dans le projet en ajoutant les getter et setter, constructor, toString, equals et hashcode :

package fr.ema.lgi2p.m2.todo;


import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Todo {

@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
private String texte;
private Boolean actif;

}

Maintenant on doit créer une base de données avec un utilisateur de BD pour ce projet, dans mysql avec phpmyadmin, faite la création d’un utilisation/MDP ayant tout les droits sur une base de données. Dans mon exemple, ces 2 valeurs seront todo.

PhpMyadmin configuration utilisateur et base de données

NOTA: Erreur suivante:

The server time zone value 'Paris, Madrid' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.

fichier Application Version 0.1

Cela vient d’une veille configuration de mon serveur mysql qui a une TimeZone double, pour corriger, soit il faut changer le réglage dans le fichier de configuration de Mysql mais j’ai trouvé plus simple en forçant dans la connexion JDBC la TimeZone:

spring.datasource.url=jdbc:mysql://localhost:3306/todo?zeroDateTimeBehavior=CONVERT_TO_NULL&serverTimezone=UTC

Une dernière exécution du projet Maven > Run > Maven install puis il faut exécuter la classe principale TodoApplication via clic droit Run > Java Application et accéder au service via un navigateur en allant sur http://127.0.0.1:8080/

Run As TodoApplication

Vous pouvez vérifier que pour l’instant aucune entité n’a été créé dans la base de données.

NOTA: il faut ajouter la dépendance suivante dans le fichier pom.xml

<dependency>
<groupId>org.threeten</groupId>
<artifactId>threetenbp</artifactId>
<version>1.3.3</version>
</dependency>

Etape 4/ Fabrication d’une classe Controleur RESTful

Créer une nouvelle Classe TodoControleur:

Spring class TodoControleur

Avec ce code qui permet de déclarer qu’une méthode est accéssible via cette URL :

package fr.ema.lgi2p.m2.todo;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TodoControleur {

@RequestMapping(value="/todos", method=RequestMethod.GET)
public String listeTodos() {
return "{todos:[]}";
}

}

Testé l’appel via l’URL : http://127.0.0.1:8080/todos

Ajoutons une méthode pour créer un todo qui aura comme texte un identifiant passé

 @RequestMapping(value = "/todo/{id}", method = RequestMethod.GET)
public Todo getTodo(@PathVariable int id) {
Todo todo = new Todo( "todo-" +id , false );

return todo; 
}

Appelons l’URL via : http://127.0.0.1:8080/todo/100

Etape 5/ Utilisation d’Hibernate au lieu de JPA pour voir un peu de code

On pourrait utiliser la couche JPA générique de Spring mais vu que nous avons une connaissance d’Hiberante et pour voir l’enchainement des opération, nous allons désactiver le JPA pour exploiter Hibernate à la place.

En premier lieu il faut configurer le fichier application.properties se trouvant dans le dossier src/main/ressources

# ===============================
# DATABASE
# ===============================

#spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

spring.datasource.url=jdbc:mysql://localhost:3306/todo?zeroDateTimeBehavior=CONVERT_TO_NULL&serverTimezone=UTC
spring.datasource.username=todo
spring.datasource.password=todo



# ===============================
# JPA / HIBERNATE
# ===============================

spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate5.SpringSessionContext

Voici la nouvelle version de la classe TodoApplication:

package fr.ema.lgi2p.m2.todo;

import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;

import java.util.Properties;

import javax.sql.DataSource;

@SpringBootApplication
@EnableAutoConfiguration(exclude = { //
DataSourceAutoConfiguration.class, //
DataSourceTransactionManagerAutoConfiguration.class, //
HibernateJpaAutoConfiguration.class })
public class TodoApplication {

@Autowired
private Environment env;

public static void main(String[] args) {
SpringApplication.run(TodoApplication.class, args);
}

@Bean(name = "dataSource")
public DataSource getDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();

// See: application.properties
dataSource.setDriverClassName(env.getProperty("spring.datasource.driver-class-name"));
dataSource.setUrl(env.getProperty("spring.datasource.url"));
dataSource.setUsername(env.getProperty("spring.datasource.username"));
dataSource.setPassword(env.getProperty("spring.datasource.password"));

System.out.println("## getDataSource: " + dataSource);

return dataSource;
}

@Autowired
@Bean(name = "sessionFactory")
public SessionFactory getSessionFactory(DataSource dataSource) throws Exception {
Properties properties = new Properties();

// See: application.properties
properties.put("hibernate.dialect", env.getProperty("spring.jpa.properties.hibernate.dialect"));
properties.put("hibernate.hbm2ddl.auto", "update");
properties.put("hibernate.show_sql", env.getProperty("spring.jpa.show-sql"));
properties.put("current_session_context_class", //
env.getProperty("spring.jpa.properties.hibernate.current_session_context_class"));

LocalSessionFactoryBean factoryBean = new LocalSessionFactoryBean();

// Package contain entity classes
factoryBean.setPackagesToScan(new String[] { "" });
factoryBean.setDataSource(dataSource);
factoryBean.setHibernateProperties(properties);
factoryBean.afterPropertiesSet();
//
SessionFactory sf = factoryBean.getObject();
System.out.println("## getSessionFactory: " + sf);
return sf;
}

@Autowired
@Bean(name = "transactionManager")
public HibernateTransactionManager getTransactionManager(SessionFactory sessionFactory) {
HibernateTransactionManager transactionManager = new HibernateTransactionManager(sessionFactory);

return transactionManager;
}


}

Cette nouvelle version permet de déconnecter les méthodes classiques du JPA pour appeler une nouvelle classe TodoDao qui va assure les opérations classiques de persistence:

package fr.ema.lgi2p.m2.todo;

import java.util.List;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.query.Query;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

@Repository
@Transactional
public class TodoDao {

@Autowired
private SessionFactory sessionFactory;

public List<Todo> listTodo() {
Session session = this.sessionFactory.getCurrentSession();
Query<Todo> query = session.createQuery("From Todo", Todo.class);
return query.getResultList();
}

public Todo newTodo(Todo newTodo) {
Session session = this.sessionFactory.getCurrentSession();
session.save(newTodo);

return newTodo;
}


}

 

Modification de la classe TodoControleur pour utiliser la classe TodoDao:

 @Autowired
private TodoDao todoDao;

 @RequestMapping(value = "/todo/{id}", method = RequestMethod.GET)
public Todo getTodo(@PathVariable int id) {
Todo todo = new Todo("todo-" + id, false);
todoDao.newTodo(todo);
return todo;
}

Maintenant l’appel à l’URL suivante : http://127.0.0.1:8080/todo/100 fabrique un nouveau todo et le fait persister.

 

Etape 6/ Static content pour Spring boot

Création d’un dossier src/main/ressources/static pour le contenu static du service qui va contenir les développements en AngularJs.

Création d’une classe IndexController qui va envoyé le contenu de index.html

package fr.ema.lgi2p.m2.todo;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class IndexController {
@RequestMapping("/")
public String welcome() {
  return "index.html";
}
}

Puis création d’un nouveau dossier src/main/ressources/static/img y placer des images et ajouter le code ci-dessous à la classe TodoApplication pour autoriser le contenu statique dans ce dossier:

 public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/img/**")
.addResourceLocations("classpath:/img/");
}

 

Etape 7/ Utilisation d’angular avec l’IDE en ligne

Appel à l’IDE proposé par Angular  https://angular.io/generated/live-examples/getting-started-v0/stackblitz.html

NOTA : cet IDE ne fonctionne pas avec Firefox, je recommande de l’utiliser avec Chrome

 

installation de NodeJS

npm install -g @angular/cli

 

Update npm on ne sait jamais (et update de nodejs peut être aussi?)

npm i npm

 

Création du projet todo dans un dossier d’un workspace dédié aux projet node-js/angularjs

ng new todo

 

Copier le dossier src téléchargés depuis le site https://angular.io/generated/live-examples/getting-started-v0/stackblitz.html dans le dossier src du projet todo en remplaçant tout le contenu.

Créer les fichiers finaux d’angularjs dans le dossier du projet spring boot et plus précisement dans le dossier ressources/static/:

ng build todo --prod  --outputPath=C:\DEV\workspace-spring\todo\src\main\resources\static

Déploiement ensuite classiquement du projet spring boot pour tester

Etape 8/ Utilisation d’angular avec l’IDE ou sans IDE

 

Debug et test du front avec création du fichier sourceMap

ng serve --sourceMap=true

Attention pour tester en local ces fichiers il faut vérifeir le fichier index.html dans le dossier dist\todo\ et corriger la balise <basehref= »/ » /> avec une URL valide. Heureusement dans notre déploiement avec spring notre application est à la racine.

Pour utiliser les fichiers finaux dans le déploiement, il suffit de copier les fichiers du dossier dist/todo/ dans le dossier src/main/ressources/static/ et appelr l’URL http://127.0.0.1:8080/

Debug avec chrome:

Il faut démarrer chrome en mode debug avec cette ligne de commande pour lancer chrome avec les outils de développement et le port d’écoute 9222 et ouvrir l’URL http://127.0.0.1:4200/

C:\Users\pierre.jean\AppData\Local\Chromium\Application\chrome.exe --auto-open-devtools-for-tabs --remote-debugging-port=9222 -incognito http://127.0.0.1:4200/

 

Pour lancer depuis Visual Code voici le fichier de configuration de launch.json:

{
    // Utilisez IntelliSense pour en savoir plus sur les attributs possibles.
    // Pointez pour afficher la description des attributs existants.
    // Pour plus d'informations, visitez : https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Chrome Launch",
            "type": "chrome",
            "request": "update",
            "runtimeExecutable":"C:/Users/pierre.jean/AppData/Local/Chromium/Application/chrome.exe",
            "runtimeArgs": [
                "--new-window",                
                "--remote-debugging-port=9222"            
              ],
            "port": 9222,
            "sourceMaps": true,
            "url": "http://localhost:4200/",
            "webRoot": "${workspaceFolder}",
            "sourceMapPathOverrides": {      
                "webpack:///./*":"${workspaceRoot}\\*" 
              }
          } 
    ]
}

 

 

NOTA ERREUR:

Error: Could not find module « @angular-devkit/build-angular »
npm update
npm install –save-dev @angular-devkit/build-angular

npm install -g @angular/cli

npm install -g @angular-devkit/build-angular

 

 

 

A propos Pierre Jean

Ingénieur de Recherche Centre de recherche LGI2P Ecole des Mines d'Alès Formation TIC&Santé Plus de détails sur Pierre JEAN
Ce contenu a été publié dans IMT Mines Alès. Vous pouvez le mettre en favoris avec ce permalien.