This project is a cloud infrastructure based on the spring-cloud projects.
Please see the CONTRIBUTING file for guidelines.
Please see the ISSUES file for guidelines.
Coding tips:
Related repositories:
Install Java Cryptography Extension (JCE) Unlimited Strength (used for OAuth2 Social Login strong encryption)
- Download the Java JCE from oracle : Oracle.
- Extract the files in JDK_HOME/jre/lib/security or JRE_HOME/lib/security.
Recommended plugins to install:
- EclEmma: Code Coverage
- MoreUnit: Helps to generate unit test code
- Pmd-plugin: Code analysis checks
- Findbugs: Helps to find potential bugs
- AnyEditTool: Export / Import working sets
- Jautodoc: Generates javadoc
- Checkstyle: Code format
- Spring-Tool-Suite: will drastically improve the debugging and startup of applications
Note: Emma is installed by default on IntelliJ IDEA
- Install CheckStyle-IDEA
- Install PMDPlugin
- Install Findbugs-IDEA
- Install the Lombok plug-in by Michail Plushnikov
- Import CheckStyle XML config (Failed! TODO: resolve issue)
-Look at the config-files project /dev-configs folder for configuration files (sql init script, checkstyle file, maven file, etc.).
Make sure you go over the Defining the APIs keys section.
- Navigate the
config-files/src/main/resources/cloud-configs/PROFILE_IN_USE(dev by default)/. - Search for all values with
**CHANGE_THIS**. - Replace the values found in 2. by an account that can access the API.
spring.mail.host=mailtrap.io
spring.mail.port=2525
spring.mail.username=**CHANGE_THIS**
spring.mail.password=**CHANGE_THIS**- Use
--as a prefix in front of any JVM argument instead of '-D', spring application will convert it to an argument. Ex:--server.port=9000or--PORT=9000 - To launch an application on linux, use the following command: 'java -Djava.security.egd=file:/dev/./urandom -jar an-application-server.jar [--server.port=2222 --anotherArg=anothervalue ...]
spring.profiles.active = activates configuration files for a specific profile (default : dev)
REGISTRY_SERVER_URI = uri of the registry server (default : http://localhost:1111/eureka/).
PORT = the server port (Can be defined on any server).
CONFIG_FILES_ROOT_LOC = location of config server files (classpath: ..., file:///..., etc.) (can only be defined on the config-server)
The project is supports Docker containers. You can launch the whole project by doing so:
Install Docker on your host machine
Increase the Docker VM memory and CPU (for windows right-click on Docker icon in the tray area and click settings, then go on Advanced).
Build the project with maven.
In a cmd prompt use
docker-compose build(this will build all docker images including the infrastructure such as SQL, Message broker, etc.)Launch the whole cloud with
docker-compose up
- RabbitMQ management : http://localhost:15672
- Sql management : http://localhost:8081
- Sql server : http://localhost:5432
- Rest-api : http://localhost:8080
Open the host file (Windows is C:\Windows\System32\drivers\etc\hosts)
Open the
docker-compose.ymlfile at the root of the source and check for thehostnamefield, the value will be the one that you need to make a redirect to 127.0.0.1.Add new entries so that the eureka instances are mapped to your localhost (normally you only need the config-service and auth-service). To get the host name you can open http://localhost:1111/ and mouse over the micro-service instance, the link should give you the name. The entry in host should look like that
127.0.0.1 docker-configYou can run the command
docker-composer up -d, it will start the containers in background, then you can stop the service you want to debugdocker-compose stop your-docker-containerStart the micro-service you want to debug from your IDE.
Some services needs to speak with others, so if you want to debug a micro-service from your machine and your environment is in docker. You need to edit the local
src/main/resources/bootstrap.ymlfile and provide two new entries: The first one iseureka.instance.preferIpAddress: trueand the second one iseureka.instance.ipAddress: THE_IP_OF_YOUR_LOCALHOST_MACHING_ON_THE_DOCKER_BRIDGE_INTERFACE. Docker by default creates a network bridge interface between the containers and your host, adding the ip to the configuration file will provide the registry server the good ip to contact from other services.
In the root of the source run
docker-compose --buildMake sure that the
docker-compose.prod.ymlfile is copied to the prod machine.Export the builded images with
docker save theshire > /some/path/to/an/output.tar.Copy the file to the production machine.
On the machine that has the source code run the following commands in a BASH window (GIT bash if on Windows) :
# Delete every Docker containers# Must be run first because images are attached to containers
docker rm -f $(docker ps -a -q)# Delete every Docker image
docker rmi -f $(docker images -q)On the production machine run
docker load -i /path/to/the/copied/output.tarOn the production machine run
docker-compose -f /path/to/docker-compose.prod.yml up -d
Create projects
Copy a launcher class from another service and put it in the ***-server project. Change the line of code to match this property 'spring.config.name' (the service name).
Copy bootstrap.properties and **.yml (rename this yml with the same name as declared in step 2) in the src/main/resources classpath.
Edit the .yml file and edit the spring.application.name to the new service name.
In the config-server project add an entry in application.properties (src/main/resources/cloud-configs) app.cloud.service.YOUR_SERVICE.serviceName.
Add a new file next to the application.properties (step 5) with the name of your service (this is the properties for your service).
In the file created from step 4, make sure you add a section with MQ-BINDINGS (check other service config-file as example).
Add the new service-id to the turbine.appConfig field in the admin-server.yml from admin-server project (src/main/resources).
Implement and give @Component and @Primary (spring-managed) annotation to the a class on your new project (YOURSERVICE-server project) that extends -> TenantInitializerDefault class and TenantDestroyerDefault class.
Add the new Dockerfile to the project.
Configure the
docker-compose.ymlfile with the new project.Configure the
docker-compose.prod.ymlfile.
- Create a simple java application launcher on the main of the Launcher class of migration-utility project.
- Provide the following arguments
mainDbUsername mainDbPassword dbDataEncryptionKey (optional: devModeEnabled)(by default for dev use:postgres postgres ddfds283nsdjahs (optional : devModeEnabled -> this option will execute dev scripts to setup dev env)).
- Here the value
ddfds283nsdjahsmustmatch the app.cloud.security.database.encryption.keyfrom theapplication-dev.propertiesfile (found in config-giles/src/main/resources/cloud-configs/).
It is possible to execute scripts dedicated for dev environment. The migration-utility will execute the scripts only in devmode.
To add a new script go to migration-utility/src/main/resources/sql-dev-scripts/. Add your script to an existing .sql file or add a new file and put your script there.
To add a new migration SQL script, you can go to migration-utility/src/main/resources/sql-scripts.
You will find a list of folders there, each of them represents the script for the different components of the application.
If you go deeper in the directory structure:
.
├── admin-service/ <- scripts for the administrative panel
├── auth-service/ <- scripts for the authorization / authentication component
│ ├── configurations/ <- scripts for the configuration schema
│ ├── sso/ <- scripts for sso schema
│ └── template/ <- scripts applied to all tenant database and the template for tenant creation
├── micro-services/ <- docker-compose file for production environment
│ ├── micro-service-name/ <- scripts for the micro-service
│ | ├── configurations/ <- scripts for the configuration schema
│ | └── template/ <- scripts applied to all tenant database and the template for tenant creation
...
Adding a new script must follow the following convention: YYYY.MM.DD_HH.MM__ScriptDescription.sql.
If you want to change/add or remove a role/permission, you can browse the migration-utility/src/main/resources/security/roles_permissions-mapper.csv and edit the .csv file.
Add a reference to common-websocket.
Create a bean that implments WebsocketSessionListener class from common-websocket and events will be automatically triggered inside your class
When subscribing or sending message to a MQ destination the following prefix rules are applied:
Sending a message that are destination-prefixed with /app on client-side will go through server otherwise it will be forwarded directly to message broker.
Subscribing to a queue from client-side should look like /user/queue/tenant.TENANTID-QUEUENAME
Subscribing to a queue from client-side should look like /topic/tenant.tenant.TENANTID-TOPICNAME
- Inject the stomp service.
@AutowiredprivateStompMessagingTemplatestompMessagingTemplate;- Create a tenant-aware route (topic or queue).
privatestaticfinalStompMessageDestinationUSER_CHAT_QUEUE_DESTINATION =
newStompMessageDestination("/queue/tenant.?-chat");
privatestaticfinalStompMessageDestinationCHAT_TOPIC_DESTINATION_PREFIX =
newStompMessageDestination("/topic/tenant.?-chat");- Send the message to a particular user or to a general topic.
// user queuestompMessagingTemplate.sendToUser(username, USER_CHAT_QUEUE_DESTINATION, message);
// general topicfinalStompMessageDestinationdestination = newStompMessageDestination(CHAT_TOPIC_DESTINATION_PREFIX.getDestination() + "-" + chatMsg.getChannelName());
stompMessagingTemplate.send(destination, message);- Create a @RestController and extend ManagedRestEndpoint class.
- Make sure you call the method buildResponse() from ManagedRestEndpoint if you want to send a message to the client.
- You should always send an object that extends TransportMessage (io.theshire.common.utils.transport.message).
- buildResponse() will handle the case where a null is provided, it will return a HTTP 404.
-To add a multi-tenant supported JPA repository, make sure to add the suffix *JpaRepository.java
publicinterfaceAccountJpaRepositoryextendsJpaSpringRepository<Account>-To add a single-tenant (will connect to the service default database) JPA repository, make sure to add the suffix *JpaSingleTenantRepository.java
publicinterfaceUserAuthenticationJpaSingleTenantRepositoryextendsJpaSpringRepository<UserAuthentication>-To inject an entity manager with the multitenancy suppport, do the following:
@PersistenceContext(unitName = JpaPersistenceUnitConstants.MULTI_TENANT_PERSISTENCE_CONTEXT)
privateEntityManagerem;-To inject an entity manager with the multitenancy suppport, do the following:
@PersistenceContext(unitName = JpaPersistenceUnitConstants.SINGLE_TENANT_PERSISTENCE_CONTEXT)
privateEntityManagerem;-Each micro-services that needs SQL should be structured as followed:
A database for the master tenant and all shared configurations (should be servicename_master).
A database for tenant creation. It uses this template database when creating a new tenant (should be servicename_template).
Several databases for tenant schema (tenant data), each of these database can contain a maximum amount of tenants (they are named *servicename_slaveXXX).

