Vous êtes sur la page 1sur 11

EMSEI CASBLANCA| 5IIR | PR.

MOHAMED YOUSSFI

Examen Architecture Distribuée JEE


Durée : 1h30 | Documents non autorisés | Annexes Fournies avec l’épreuve
On souhaite créer un système distribué nommé « INFRACTIONS » basé sur les micro-services en
utilisant Spring Cloud qui permet gérer le processus des infractions routières concernant des véhicules
suites à des dépassement de vitesses détectés par des radars automatiques. Le système se compose
de trois micro-services :

• Le micro-service qui permet de gérer les radars. Chaque radar est défini par son id, sa vitesse
maximale, des coordonnées : Longitude et Latitude.
• Le micro-service d'immatriculation qui permet de gérer des véhicules appartenant à des
propriétaires. Chaque véhicule appartient à un seul propriétaire. Un propriétaire est défini par
son id, son nom, sa date de naissance, son email et son email. Un véhicule est défini par son
id, son numéro de matricule, sa marque, sa puissance fiscale et son modèle.
• Le micro-service qui permet de gérer les infractions. Chaque infraction est définie par son id,
sa date, le numéro du radar qui a détecté le dépassement, la vitesse du véhicule, la vitesse
maximale du radar et le montant de l'infraction. Chaque infraction concerne un véhicule

Les données des micro-services sont stockées dans des bases de données de type H2. La messagerie
asynchrone entre les micro-services se fait en utilisant le Broker KAFKA. La communication synchrone
entre les micro-services fonctionnels se fait en utilisant Open Feign. La partie back end de l’application
est basée sur Spring Cloud et la partie Frontend Web est basée sur Angular ou React. La sécurité est
basée sur Oauth2, Open Id Connect avec Keycloak

Cette épreuve se compose de trois parties :

A. Partie QCM qui concerne des questions de concepts qui n’est pas liée à ce problème
B. Partie Conception qui est liée à ce problème
C. Partie Implémentation qui est liée à ce problème

Des annexes contenant des extraits de codes est fournie à partir de la page 7 jusqu’à la page 11. Ces
annexes contiennent des exemples de codes sources qui n’ont aucun lien avec le problème traité dans
cet examen. Toutefois, quelques annexes peuvent vous servir pour des consultations syntaxiques

1
EMSEI CASBLANCA| 5IIR | PR. MOHAMED YOUSSFI

Travail demandé :
A. Partie QCM :
• Barème : 1 pour une bonne réponse et 0 pour une mauvaise réponse
• Mode de réponse : Les réponses doivent être portées directement sur la feuille de réponse
de l’examen en respectant le format suivant par exemple :
§ Question 1 : A
§ Question 8 : B , C
Ce qui signifie que vous avez choisi la réponse A pour la question 1 et les réponses B et C
pour la question 8.
----------------------------------------------------------------------------------------------------------------------------------
1. Quelles sont les différentes solutions qui permettent de faire communiquer des systèmes
Distribués (Plusieurs réponses sont possibles) ?
A. Web services D. CORBA
B. RMI E. JMS
C. JPA F. GRPC
2. Quelle est la spécification JEE qui permet d’implémenter les web services basés sur le
protocole SOAP ?
A. JAXRS C. JMS
B. JAXWS D. SoapWS
3. Quelle est la spécification JEE qui permet d’implémenter les web services basés sur
RESTFul ?
E. JAXRS G. JMS
F. JAXWS H. SoapWS
4. Dans les systèmes distribués, quelques les solutions qui permettent aux applications
distribuées de communiquer avec des messages de manière asynchrone (Plusieurs
réponses sont possibles) ?
A. SOAP D. RabbitMQ
B. RMI E. KAFKA
C. JMS

5. Quelle est la définition qui est correcte pour le WSDL ?


A. Le WSDL est un document XML, qui permet qui permet de sécuriser un Web service
B. Le WSDL est un document XML utilisant les schémas XML, qui permet de faire la
description de l’interface d’un web services
C. Le WSDL est un document JSON, qui permet de faire la description de l’interface d’un web
services
D. Le WSDL est un document PDF, qui permet de faire la description de l’interface d’un web
services

6. Pour implémenter un web service basé sur JAXWS, quelle est l’annotation à utiliser sur la
classe du web service ?
A. @Service C. @SoapService
B. @WebService D. @Path

7. Dans Spring cloud, quel est le service qui permet d’implémenter le Discovery Service ?
A. Spring Cloud Discovery C. Netflix Hystrix
B. Eureka Discovery D. Spring UDDI

2
EMSEI CASBLANCA| 5IIR | PR. MOHAMED YOUSSFI

8. Quelles sont les trois parties qui composent un JWT (Json Web Token) ?
A. Header, Payload et Signature C. SOAP, WSDL et UDDI
B. Header, Contract, Signature D. Contrat, Payload et Signature
9. Comment est calculée la signature d’un JWT ?
A. La signature est calculée en fonction du Header, Payload et la clé publique
B. La signature est calculée en fonction du Header, Payload et la clé privée
C. La signature est calculée en fonction du Header, Payload et les deux clés : publique et
privée
D. La signature est calculée en fonction Payload et la clé privée
10. Quand un utilisateur est authentifié via Keycloak, l’application frontend reçoit un Token
qui contient :
A. Un JWT Access Token
B. Un JWT Refresh Token
C. Un JWT Access Token et un JWT Refresh Token
D. Un simple Token

11. Quelle est l’annotation de Spring MVC qui permet de déployer un web service RESTFUL
sous forme d’un contrôleur ?
A. @RestService C. @RepositoryRestResource
B. @RestController D. @RestResource

3
EMSEI CASBLANCA| 5IIR | PR. MOHAMED YOUSSFI

Pour les questions QCM suivantes, on considère le code suivant (ANNEXE_QCM) d’un simple micro-
service basé sur Spring Boot permettant de gérer des comptes en utilisant les dépendances Spring
Data JPA, H2, Spring Web, Rest Respositories, Lombok et Dev Tools. On suppose que tous les
éléments de l’application sont dans le même package, que le fichier de configuration
application.properties est vide et que le port 8080 est libre

/*******************************************/
/************* ANNEXE_QCM *********************/ ANNEXE1
/*******************************************/
@Entity @Data @NoArgsConstructor @AllArgsConstructor @ToString public enum TypeCompte{
public
@Entity class
@Data Compte{
@NoArgsConstructor @AllArgsConstructor @ToString public COURANT,EPARGNE
enum TypeCompte{
@Id Compte{
public class } COURANT,EPARGNE
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) }
private Long id;
@GeneratedValue(strategy = GenerationType.IDENTITY)
private
private Longdouble
id; solde; private Date dateCreation;
private
private long
double contractID;
solde; private
private Date TypeCompte type;
dateCreation;
}
private long contractID; private TypeCompte type;
}
@RepositoryRestResource
@RepositoryRestResource
public interface CompteRepository extends JpaRepository<Compte,Long> {
public interface CompteRepository
List<Compte> extends JpaRepository<Compte,Long>
findByType(TypeCompte typeCompte); {
List<Compte> findByType(TypeCompte
List<Compte> typeCompte);
findByContractID(long contratID);
}List<Compte> findByContractID(long contratID);
}
@SpringBootApplication
public class ServiceCompteApplication extends SpringBootServletInitializer {
@SpringBootApplication
public@Override
class ServiceCompteApplication extends SpringBootServletInitializer {
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
@Override
return
protected builder.sources(ServiceCompteApplication.class);
SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
} return builder.sources(ServiceCompteApplication.class);
} public static void main(String[] args) {
publicSpringApplication.run(ServiceCompteApplication.class,
static void main(String[] args) { args);
} SpringApplication.run(ServiceCompteApplication.class, args);
}
@Bean
CommandLineRunner start(CompteRepository compteRepository){
@Bean
return args ->
CommandLineRunner {
start(CompteRepository compteRepository){
compteRepository.save(new
return args -> { Compte(null,Math.random(),new Date(),1,TypeCompte.COURANT)) ;
compteRepository.save(new
compteRepository.save(new Compte(null,Math.random(),new
Compte(null,Math.random(),new Date(),2,TypeCompte.COURANT))
Date(),1,TypeCompte.COURANT)) ; ;
compteRepository.save(newCompte(null,Math.random(),new
compteRepository.save(new Compte(null,Math.random(),newDate(),2,TypeCompte.COURANT))
Date(),1,TypeCompte.EPARGNE)) ; ;
}; compteRepository.save(new Compte(null,Math.random(),new Date(),1,TypeCompte.EPARGNE)) ;
} };
}
}
}

4
EMSEI CASBLANCA| 5IIR | PR. MOHAMED YOUSSFI

Questions relatives à l’annexe de la page précédente (ANNEXES_QCM) :

12. Est-ce que le code de cette application (ANNEXE_QCM) est suffisant de générer au démarrage
la table comptes dans la base de données H2

A. OUI B. NON
13. Est-ce que le code de cette application (ANNEXE_QCM) permet de démarrer un web service
basé sur SOAP, WSDL qui permet d’accéder aux opérations classiques des gestions des
comptes.
A. OUI B. NON
14. Est-ce que le code de cette application (ANNEXE_QCM) permet de démarrer automatiquement
un conteneur Web embarqué TOMCAT
A. OUI B. NON
15. Dans le code de l’ ANNEXE_QCM, Quelles sont les annotations de JPA qui permettent de rendre
la classe Compte persistante
A. @Entity E. @Data
B. @Data F. @NoArgsConstructor
C. @Id G. @AllArgsConstructor
D. @GenratedValue H. @ToString
16. Dans le code de l’ ANNEXE_QCM, Quelles sont les annotations de JPA qui est l’annotation qui
permet générer les getters et setters
A. @Entity E. @Data
B. @Data F. @NoArgsConstructor
C. @Id G. @AllArgsConstructor
D. @GenratedValue H. @ToString
17. Dans le code de l’ANNEXE1, Quelle est la classe ou l’interface qui permet d’intégrer Spring Data
pour faire le mapping objet relationnel
A. Compte C. EtatCompte
B. CompreRepository D. ServiceCompteAppplication
18. Dans le code de l’ ANNEXE_QCM, Quelle est l’annotation Spring Data Rest qui permet
d’exposer une API Restfull permettant d’accéder aux opérations de gestion des comptes
intégrées par Spring Data
A. @Entity C. @SpringBootApplication
B. @RepositoryRestResource D. @Bean
19. En exploitant l’API Restful exposée par Spring Data Rest de l’application de l’ANNEXE_QCM,
Quels types de requête http qui permet de consulter au format JSON les 10 premiers
enregistrements de la table compte.
A. GET http://localhost:8080/comptes
B. GET http://localhost:8080/pageComptes?size=10
C. GET http://localhost:8080/comptes?page=0&size=10
D. GET http://localhost:8080/comptes?size=10
20. Quelle est la propriété à ajouter dans le fichier application.properties pour fixer le port du
conteneur web du micro-service à 8888
A. spring.port=8888
B. spring.server.port=8888
C. server.port=8888
D. serverPort=8888
E. spring.serverPort=8888

5
EMSEI CASBLANCA| 5IIR | PR. MOHAMED YOUSSFI

-------------------------------------------------------------------------------------------------------------------------------------
Les questions suivantes concernent le projet « INFRACTION » qui a été décrit au début de
l’énoncé de l’épreuve.

B. Partie Conception :
1. Etablir une architecture technique du projet qui montre l’ensemble des micro-
services de l’application
2. Etablir un diagramme de classes qui montre les entités métier de l’application.
C. Partie Implémentation :
1. En utilisant JPA, Spring Data, Spring Data Rest, et Lombok, écrire le code du micro-
service IMATRICULATION-SERVICE en implémentant les éléments suivants :
a. Code des entités JPA Vehicule et Proprietaire
b. Interface JPA Repository basée sur Spring Data
c. API Restful basée sur Spring Data Rest
2. En utilisant Spring Data, Spring Data Rest, et Lombok, et Open Feign, écrire le code
du micro-service INFRACTION-SERVICE en implémentant les éléments suivants :
a. Code source de l’entité Infraction
b. Interface JPA Repository basée sur Spring Data
c. Interface OpenFeign permettant de consulter un véhicule sachant son id.
d. Un Web service Restful basée sur RestController qui permet de consulter les
données complètes d’une infraction sachant son id.
3. Partie KAFKA et Spring cloud Streams
a. Écrire le code d’un Producer KAFKA qui permet de produire aléatoirement
des Infractions et de les publier dans un topic KAFKA
b. Écrire le code d’un Consumer KAFKA qui consomme les infractions publiées
dans le topic KAFKA et de les enregistrer dans la base de données.
c. En utilisant KAFKA Streams, écrire le code d’un consumer KAFKA qui permet
de faire du Real Time Stream Processing Data Analytics en consommant le
stream des infractions du topic KAFKA en calculant le moyenne des vitesses
des infractions de chaque radar.

6
EMSEI CASBLANCA| 5IIR | PR. MOHAMED YOUSSFI

ANNEXES
Ces annexes contiennent des exemples de codes sources qui n’ont aucun lien avec le problème traité
dans cet examen. Toutefois, quelques annexes peuvent vous servir pour des consultations syntaxiques.
-----------------------------------------------------------------------------
@Entity
@Data @NoArgsConstructor @AllArgsConstructor @ToString
class Product{
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private double price;
}
-----------------------------------------------------------------------------
<div class="container">
<button *ngIf="authService.isAdmin()" class="btn btn-danger"
(click)="onNewTask()">New Task</button>
<table class="table table-striped">
<thead>
<tr>
<th>ID</th><th>Task Name</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let t of tasks">
<td>{{t.id}}</td>
<td>{{t.taskName}}</td>
</tr>
</tbody>
</table>

</div>

-----------------------------------------------------------------------------
@RepositoryRestResource
interface ProductRepository extends JpaRepository<Product,Long>{
}
-----------------------------------------------------------------------------
@Document
@Data @AllArgsConstructor @NoArgsConstructor
public class Category {
@Id
private String id;
private String name;
@DBRef
private Collection<Product> products=new ArrayList<>();

}
-----------------------------------------------------------------------------
import {JwtHelper} from 'angular2-jwt';
@Injectable()
export class AuthenticationService{
private host:string="http://localhost:8080";
private jwtToken:string;
private roles:Array<any>=[];
constructor(private http:HttpClient){}

getTasks(){
if(this.jwtToken==null) this.loadToken();

7
EMSEI CASBLANCA| 5IIR | PR. MOHAMED YOUSSFI

return this.http.get(this.host+"/tasks",{headers:new
HttpHeaders({'authorization':this.jwtToken})});
}
logout(){
localStorage.removeItem('token');
}
isAdmin(){
for(let r of this.roles){
if(r.authority=='ADMIN') return true;
}
return false;
}
}
-----------------------------------------------------------------------------
@Entity
@Data @NoArgsConstructor @AllArgsConstructor
class Bill{
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Date billingDate;
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private Long customerID;
@Transient private Customer customer;
@OneToMany(mappedBy = "bill")
private Collection<ProductItem> productItems;
}
-----------------------------------------------------------------------------
@RepositoryRestResource
interface BillRepository extends JpaRepository<Bill,Long>{

}
-----------------------------------------------------------------------------
@Projection(name="fullBill",types = Bill.class)
interface BillProjection{
public Long getId();
public Date getBillingDate();
public Long getCustomerID();
public Collection<ProductItem> getProductItems();
}
-----------------------------------------------------------------------------
@Entity
@Data @NoArgsConstructor @AllArgsConstructor
class ProductItem{
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private Long productID;
@Transient private Product product;
private double price;
private double quantity;
@ManyToOne
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private Bill bill;
}
-----------------------------------------------------------------------------
@Component
public class Configuration {
@Autowired
private EtudiantRepositoy etudiantRepositoy;

8
EMSEI CASBLANCA| 5IIR | PR. MOHAMED YOUSSFI

@JmsListener(destination="scolarite.queue")

public void receive(byte[] message) throws Exception {

String strMessage=new String(message,"UTF-8");


System.out.println(strMessage);
ObjectMapper objectMapper=new ObjectMapper();
Etudiant et=objectMapper.readValue(message, Etudiant.class);
System.out.println("Nom:"+et.getNom());
System.out.println("Prénom:"+et.getPrenom());
etudiantRepositoy.save(et);
}
-----------------------------------------------------------------------------
@RepositoryRestResource
interface ProductItemRepository extends JpaRepository<ProductItem,Long>{

}
-----------------------------------------------------------------------------
@Data
class Customer{
private Long id; private String name; private String email;
}
-----------------------------------------------------------------------------
@FeignClient(name = "CUSTOMER-SERVICE")
interface CustomerService{
@GetMapping("/customers/{id}")
public Customer findCustomerById(@PathVariable(name="id") Long id);
}
-----------------------------------------------------------------------------
@Data
class Product{
private Long id;
private String name;
private double price;
}
-----------------------------------------------------------------------------
@FeignClient(name="INVENTORY-SERVICE")
interface InventoryService{
@GetMapping("/products/{id}")
public Product findProductById(@PathVariable(name="id") Long id);
@GetMapping("/products")
public PagedModel<Product> findAllProducts();
}

-----------------------------------------------------------------------------

@RestController
public class Catalogue {
@Autowired
private ProduitRepository produitRepository;

@RequestMapping(value="/produits",method=RequestMethod.GET)
public List<Produit> listProduits(){
return produitRepository.findAll();
}
@RequestMapping(value="/produits/{id}",method=RequestMethod.GET)
public Produit getProduit(@PathVariable(name="id")Long id){

9
EMSEI CASBLANCA| 5IIR | PR. MOHAMED YOUSSFI

return produitRepository.findOne(id);
}
@RequestMapping(value="/produits",method=RequestMethod.POST)
public Produit save(@RequestBody Produit p){
return produitRepository.save(p);

-----------------------------------------------------------------------------
@RestController
class BillRestController{
@Autowired
private BillRepository billRepository;
@Autowired
private ProductItemRepository productItemRepository;
@Autowired
private CustomerService customerService;
@Autowired
private InventoryService inventoryService;

@GetMapping("/fullBill/{id}")

public Bill getBill(@PathVariable(name="id") Long id){


Bill bill=billRepository.findById(id).get();
bill.setCustomer(customerService.findCustomerById(bill.getCustomerID()));
bill.getProductItems().forEach(pi->{
pi.setProduct(inventoryService.findProductById(pi.getProductID()));
});
return bill;
}

}
-----------------------------------------------------------------------------

@SpringBootApplication
@EnableEurekaServer
public class EurekaServiceApplication {

public static void main(String[] args) {


SpringApplication.run(EurekaServiceApplication.class, args);
}

-----------------------------------------------------------------------------
@RepositoryRestResource
public interface ProductRepository extends MongoRepository<Product,String> {
public List<Product> findByNameContains(String motCle);
}

-----------------------------------------------------------------------------
@Bean
DiscoveryClientRouteDefinitionLocator dynamicRoutes(ReactiveDiscoveryClient rdc,
DiscoveryLocatorProperties dlp){
return new DiscoveryClientRouteDefinitionLocator(rdc,dlp);
}

10
EMSEI CASBLANCA| 5IIR | PR. MOHAMED YOUSSFI

-----------------------------------------------------------------------------
@Component
@WebService
public class Scolarite {
@Autowired
private EtudiantRepositoy etudiantRepositoy;
@Autowired
private FormationRepository formationRepository;
@WebMethod(operationName="listEtudiants")
public List<Etudiant> list(){
return etudiantRepositoy.findAll();
}
@WebMethod
public Etudiant getOne(@WebParam(name="id") Long id) {
return etudiantRepositoy.findOne(id);
}
@WebMethod
public Formation save(@WebParam(name="formation")Formation f) {
return formationRepository.save(f);
}
}
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
@Bean
public Supplier<PageEvent> pageEventSupplier(){
return ()->{
return PageEvent.builder()
.name((Math.random()>0.5)?"P1":"P2")
.user((Math.random()>0.5)?"U1":"U2")
.date(new Date())
.duration(new Random().nextInt(1000))
.build();
};
}
@Bean
public Function<KStream<String, PageEvent>,KStream<String, Double>>
kStreamFunction2(){
return (input) -> input
//.filter((k,v)->v.getDuration()>100)
.map((k,v)->new KeyValue<>(v.getName(),(double)v.getDuration()))
.groupBy((k,v)->k, Grouped.with(Serdes.String(),Serdes.Double()))
.windowedBy(TimeWindows.of(Duration.ofSeconds(30)))
//.count(Materialized.as("count-store"))
.aggregate(()->0.0, (k,v,total)->total+v,Materialized.as("total-store"))
.toStream()
.map((k,v)->new KeyValue<>(k.key().toString(),v));
}

spring.kafka.streams.application-id=app2
spring.cloud.stream.kafka.streams.binder.configuration.commit.interval.ms=5000
spring.cloud.stream.kafka.streams.binder.configuration.default.key.serde=org.apache
.kafka.common.serialization.Serdes$StringSerde
spring.cloud.stream.kafka.streams.binder.configuration.default.value.serde=org.apac
he.kafka.common.serialization.Serdes$DoubleSerde
spring.kafka.producer.key-
serializer=org.apache.kafka.common.serialization.StringSerializer

11

Vous aimerez peut-être aussi