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-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.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 --build -
Make 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.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.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.
@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).

