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=9000
or--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.yml
file at the root of the source and check for thehostname
field, 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-config
-
You 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-container
-
Start 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.yml
file and provide two new entries: The first one iseureka.instance.preferIpAddress: true
and 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 --build
-
Make sure that the
docker-compose.prod.yml
file 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.tar
-
On 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.yml
file with the new project. -
Configure the
docker-compose.prod.yml
file.
- 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
ddfds283nsdjahs
mustmatch the app.cloud.security.database.encryption.key
from theapplication-dev.properties
file (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.
@Autowired
private StompMessagingTemplate stompMessagingTemplate;
- Create a tenant-aware route (topic or queue).
private static final StompMessageDestination USER_CHAT_QUEUE_DESTINATION =
new StompMessageDestination("/queue/tenant.?-chat");
private static final StompMessageDestination CHAT_TOPIC_DESTINATION_PREFIX =
new StompMessageDestination("/topic/tenant.?-chat");
- Send the message to a particular user or to a general topic.
// user queue
stompMessagingTemplate.sendToUser(username, USER_CHAT_QUEUE_DESTINATION, message);
// general topic
final StompMessageDestination destination = new StompMessageDestination(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
public interface AccountJpaRepository extends JpaSpringRepository<Account>
-To add a single-tenant (will connect to the service default database) JPA repository, make sure to add the suffix *JpaSingleTenantRepository.java
public interface UserAuthenticationJpaSingleTenantRepository extends JpaSpringRepository<UserAuthentication>
-To inject an entity manager with the multitenancy suppport, do the following:
@PersistenceContext(unitName = JpaPersistenceUnitConstants.MULTI_TENANT_PERSISTENCE_CONTEXT)
private EntityManager em;
-To inject an entity manager with the multitenancy suppport, do the following:
@PersistenceContext(unitName = JpaPersistenceUnitConstants.SINGLE_TENANT_PERSISTENCE_CONTEXT)
private EntityManager em;
-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).