Skip to content
This repository has been archived by the owner on May 10, 2019. It is now read-only.

Commit

Permalink
Merge pull request #36 from Skullabs/release/2.0
Browse files Browse the repository at this point in the history
Release/2.0
  • Loading branch information
miere committed Jul 27, 2015
2 parents ae2404f + a5a7f62 commit d8c82e5
Show file tree
Hide file tree
Showing 152 changed files with 3,494 additions and 2,399 deletions.
313 changes: 290 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,296 @@
# tRip - Low coupling module development
tRip intent to be a practical and lightweight tool to provide modularity and low coupling on your source code.

### Main Features
tRip was designed to:
- Take care of Singleton's and Stateless' services for your
- Allow you to create modules and extensions to your software without change one LOC in the core implementation
- Fast warm-up: tRip already knows what to provide in compilation phase, there's no need to look into the entire class-path for provided classes
- Zero configuration: just let the [ServiceProvider](https://github.com/Skullabs/tRip/wiki/Getting-Started:-My-First-Modular-App) run the software for you
- Factory-based creation of services: you can take control of how a service is provided creating your own factory
- Manually provided data: you still can provide data manually to your software context.

### Low footprint
tRip is basically two jars:
- trip-core ( 30kb ): which is needed to run your application
- trip-processor ( 28kb ): which is responsible by the auto discovery of Singleton's and Stateless' services on your modules. This dependency is needed only during compilation phase.

### Lets get started?
Here is a little tour of tRip main features.
- [Configuring a project with tRip](https://github.com/Skullabs/tRip/wiki/Configuring-a-project-with-tRip)
- [My First Modular App](https://github.com/Skullabs/tRip/wiki/Getting-Started:-My-First-Modular-App)
- [Understanding ServiceProvider](https://github.com/Skullabs/tRip/wiki/Undertanding-ServiceProvider)
- [The Injection Process](https://github.com/Skullabs/tRip/wiki/The-Injection-Process)
- [Provinding Services](https://github.com/Skullabs/tRip/wiki/Providing-Services)
- [Automatically](https://github.com/Skullabs/tRip/wiki/Providing-Services:-Automatically-Way)
- [Manually](https://github.com/Skullabs/tRip/wiki/Providing-Services:-Manually-Way)
## Main Features
- Designed to help developers to create modular micro-services in Java.
- Compile time discovery of Service Provider Interfaces (SPI)
- Optimized runtime injection of services (discovered SPIs)
- Orthogonal factory pattern (Similar to ```@Produces``` from JSR299)
- CDI (JSR 330) compliant API
- Very low memory footprint
- Lightweight dependency stack (1 JAR -> ~35kb)

### 1. Configuring a Maven Project
Just include the following libraries to your pom.xml.
```xml
<!-- Main tRip dependency -->
<dependency>
<groupId>io.skullabs.trip</groupId>
<artifactId>trip-core</artifactId>
<version>${version.trip}</version>
</dependency>
<!-- The APT that will discovery your SPIs -->
<dependency>
<groupId>io.skullabs.trip</groupId>
<artifactId>trip-processor</artifactId>
<version>${version.trip}</version>
<scope>provided</scope>
</dependency>
```
If you intent to use CDI (JSR330) annotations, please include the following library too.
```xml
<dependency>
<groupId>io.skullabs.trip</groupId>
<artifactId>trip-core</artifactId>
<version>${version.trip}</version>
<!-- it could be 'provided' if you have included javax.inject manually -->
<!-- scope>provided</scope -->
</dependency>
```

### 2. Providing Services
Bellow some examples how you can make a class available to the _tRip Context_.
```java
import trip.spi.*;

public interface Player {}

@Singleton( exposedAs=Player.class )
// will expose Hero as a Player
public class Hero implements Player {}

@Stateless( exposedAs=Player.class )
// will expose BadBoy as a Player, managed as Stateless
public class BadBoy implements Player {}

@Singleton
public class Game {}
```

### 3. Injecting Provided Services
```java
import trip.spi.*;

public interface Player {}
public interface Weapon {}

@Singleton( exposedAs=Weapon.class )
public class Granade implements Weapon {}

@Singleton( exposedAs=Player.class )
public class BadBoy implements Player {

@Provided Weapon weapon;

}
```
You can inject concrete implementations into your services. It is not required to
expose services as interfaces (or abstract classes).
```java
// exposing itself as a service
@Singleton
public class Menu {}

// injecting the concrete implementation
@Singleton
public class Game {
@Provided Menu menu;
}
```

#### 3.1 Injecting all implementations of a service
Sometimes it would be nice if was possible to retrieve all implementations of a specific
interface and have them injected into your controller. tRip provides this functionality through
the ```@ProvidedServices``` annotation as illustrated bellow.
```java
import trip.spi.*;

public interface Player {}

@Singleton( exposedAs=Player.class )
// will expose Hero as a Player
public class Hero implements Player {}

@Stateless( exposedAs=Player.class )
// will expose BadBoy as a Player, managed as Stateless
public class BadBoy implements Player {}

@Singleton
public class Game {

@ProvidedServices( exposedAs=Player.class )
Iterable<Player> allAvailablePlayers;

}
```

#### 3.2. Producing data orthogonally
Is possible to produce data to be injected using a factory pattern. To achieve this, tRip provides the __producer method__ approach. A producer method generates an object that can then be injected. Typically, you use producer methods in the following situations:
- When you want to inject an object that is not itself a bean
- When the concrete type of the object to be injected may vary at runtime
- When the object requires some custom initialization that the bean constructor does not perform.

```java
@Singleton
public class DatabaseAccess {

@Provided DatabaseConfig dbConfig;

public Connection getConnection(){
// a method that uses dbConfig to retrieve a database connection
}
}

@Singleton
public class DBConfigProducer {

@Producer
public DatabaseConfig produceDbConfig(){
// produce dbConfig for further usage
}
}
```
The above example illustrates how is possible to produce a DatabaseConfig at runtime. Everytime a service request for a ```DatabaseConfig``` dependency, the ```DBConfigProducer.produceDbConfig``` method will be called. It means:
- it will be called once per Singleton instance
- it will be called every time a Stateless' service public method is called

#### 3.3 Disambiguation
Sometimes we have more than one dependency provided with some Interface. It is possible to
create a qualifier annotation (in a JSR330 way), to identify which implementation should
be injected.
```java
import trip.spi.*;

public interface Weapon {}

@Qualifier
@Retention( RUNTIME )
public @interface HiDamage {}

@HiDamage
@Singleton( exposedAs=Weapon.class )
public class Granade implements Weapon {}

@Singleton( exposedAs=Weapon.class )
public class Gun implements Weapon {}

@Singleton( exposedAs=Player.class )
public class BadBoy implements Player {

@HiDamage
@Provided
Weapon weapon;

}

// You can always inject a specific concrete implementation
@Singleton( exposedAs=Player.class )
public class BadBoy implements Player {

@Provided(exposedAs=Weapon.class)
Gun gun;

}
```

### 4. Managing (injecting into) classes with tRip Context
You should have at least one managed class to have its dependencies injected. tRip have
a context implementation called ```trip.spi.ServiceProvide```. It controls all injectable data,
are capable to handle singletons and stateless services, and instantiate classes when it is
required as a dependency but it neither is a singleton nor a stateless implementation.
```java
import trip.spi.*;

@Singleton
public class Game { /* ... */ }

public class Main {

public static void main( String[] args ){
ServiceProvider provider = new DefaultServiceProvider();
Game game = provider.load( Game.class );
game.run();
}
}
```
The above example code shows how is possible to intialize the ```Game``` class
and have its dependencies injected. The dependencies' dependencies will be recursively injected.
In this case, will be only instance of ```Game``` no matter how many times ```ServiceProvider.load```
is called. If neither ```@Stateless``` nor ```@Singleton``` annotations is present, then a new instance
of Game will be created everytime ```ServiceProvider.load``` is called.

It is also possible to inject data in an already created instance of any class.
```java
public static void main( String[] args ){
ServiceProvider provider = new DefaultServiceProvider();
Game game = new Game();
provider.provideOn( game );
game.run();
}
```

To avoid side effects on your application, there should be only one instance of ServiceProvider on your
project. If you need access to current tRip Context you should inject it on your code.
```java
public class AClassThatUsesServiceProvider {

@Provided ServiceProvider provider;

}
```

#### 4.1. Stateless Services
Stateless services, as it name suggests, is a services that does not have state. It means that it does not stores data between two or more executions of a public method. Its simple to be handled by developers, once it is thread-safe by default ( except if this service is manually shared between threads ). The bellow source code illustrates how it works.
```java
public class User {}

public class DatabaseAccess {}

@Stateless
public class UserPersistenceService {

@Provided DatabaseAccess dao;

public void persist( User user ){
dao.newTransaction();
dao.persist( user );
dao.commit();
}
}

@Singleton
// exposed as REST endpoint by your favorite REST framework
public class MyRESTEndpoint {

@Provided UserPersistenceService userService;

@POST @Path( "/api/user" )
public void persistUser( User user ){
userService.persist( user );
}
}
```
At the above sample code, every time the method ```UserPersistenceService.persist``` is called, a new instance of ```UserPersistenceService``` will be created, all its dependencies will be injected again and then the method you called will be executed. It makes easier to handle transactions in a clean way, as the above sample states.

As ```Stateless``` services have a short life cycle, you can use the JDK ```PostConstruct``` and ```PreDestroy``` annotations to be notified, respectively, when the service is constructed and destructed before every call to a public service's method.

#### 4.2. Singleton Services
It is a managed service that appears once in the context. Every consumer will receive the same instance of this service. Is assigned to the developer the responsibility to keep the service consistent and, if is the case, thread-safe. No secret here. Singletons services are memory friendly, but requires a more sophisticated design to avoid problems in a multi-threaded environment like a web server.

Opposed to stateless services, ```Singleton``` services have a long life cycle. Thus tRip only supports the JDK's ```PostConstruct``` annotation. It is called every time a singleton service is called.

### Using CDI (JSR-330) Annotations
The Java CDI API (JSR-330) was designed to be a simple and intuitive Inversion of Control API. It is based on annotations and behaves very similar to tRip native SPI API. Since tRip 2.x version, tRip offers a JSR-330 support and also the ```@javax.enterprise.inject.Produces```. Bellow a comparison table between each JSR-330 annotations and tRip JSR-330 annotations.

|Functionality|tRip|CDI JSR-330|
|:-------------|:-------------:|:-------------:|
|Singleton services|trip.spi.Singleton|javax.inject.Singleton|
|Stateless services|trip.spi.Stateless|N/A|
|Inject a service into a field|trip.spi.Provided|javax.inject.Inject|
|Inject all services into a field|trip.spi.ProvidedServices|N/A|
|Producer/factory method pattern|trip.spi.Producer|javax.enterprise.inject.Produces (JSR-299)|

#### A note about ```@javax.inject.Singleton``` annotated services
The default tRip's ```@Singleton``` annotation will expose only the interface(or abstract class) defined
on ```Singleton.exposedAs``` attribute, or the concrete class itself if no attribute is set at all.

When you are using JSR-330's ```@Singleton``` annotation, is expected that the container should be able
to inject corretly a service (either by the concrete class or through the interface) automatically. tRip
JSR-330 respect this rule and correctly injects the needed service as defined by the JSR-330 spec.

### Contributing
tRip is a open source software. It means any help, improvement or bug fixed are welcome. Even typos on
this documentation is a very welcome improvement.

### License
tRip is Apache 2.0 licensed.
Loading

0 comments on commit d8c82e5

Please sign in to comment.