Component

Definition

Jmine’s most basic artifact, defines basic component structure, exceptions and internationalization support.

Component Structure

There are currently 2 types of component structure. The Classic approach has been used since the beginning, but is gradually being replaced by a more isolated approach. Here the current approach will be described, with the Classic one described in the sub-section below.

A Component is an isolated, self-contained piece of software that provides implementations of services that can be used by external API users. A Component’s design should be very careful, and much thought should be given to API design: an API should be easy to use, minimal and safe. A bad API will not be easy to use, and could easily hinder further development and maintenance in the component. A Component’s lifecycle is managed by the ComponentProvider, that acts as a repository for all available components, creating them when needed.

Spring Based Component Structure

It is recommended, but not required, to implement a component using the Spring Framework for container definition and dependency injection. We’ll describe the construction of a sample component to illustrate key points.

First, one should create 2 maven projects - sample-api and sample-runtime. The API will contain only the necessary interfaces and beans to be used for input and output, the runtime module will contain the necessary implementation.

The sample-api module must have a class that will act as its Key:

package jmine.sample.api;

@ComponentKey(name = "sample")
@ComponentRoot(artifactId = "sample-api")
public interface SampleKey {
}

In the above example, the ComponentKey annotation defines this class as key of the component named “sample”. This class will be used by the API’s user to ask the ComponentProvider for the Component‘s instance. The ComponentRoot annotation, that contains the Maven artifact’s id for the api project, will be used for package scanning purposes, marking the class’s package and artifact as the root to be scanned for service interfaces.

Finally, the API must contain the component’s services:

package jmine.sample.api;

@Public
public interface SampleService {
        void sayHello(String toWhom);
}

A service’s API is any interface with the annotation Public. There is no further restriction.

With the API done, the implementation follows in the sample-runtime project:

package jmine.sample.runtime;

@Service
public class SampleServiceRuntime implements SampleService {
        void sayHello(String toWhom) {
                System.out.println("Hello, " + toWhom);
        }
}

The Service annotation denotes that this is a singleton in the component’s runtime context, that will be created by Spring during the runtime’s scanning. Also, to be able to scan, another component root must be defined, just as in the API.

package jmine.sample.runtime;

@ComponentRoot(artifactId = "sample-runtime")
public interface RuntimeRoot {
}

The coding is done, now for some configuration. The following XML block is enough to do the necessary wiring:

<bean class="jmine.tec.component.services.spring.InstallComponentBeanFactoryPostProcessor">
        <property name="componentKey" value="jmine.sample.api.SampleKey"/>
        <property name="componentRoots" value="jmine.sample.runtime.RuntimeRoot"/>
</bean>

This will create a factory for your component, that will be available in the ComponentProvider, and expose your service with the id sample.sampleService (<component name>.<service class name, with lower case for the first character>.

With the initial set up done, no further configurations should be required. Classpath scanning should handle new services and runtime objects.

Components are also able to provide Jmine Services (as defined by the Services module) in a simple way for ease of use or as a way to complement the APIs the component provides. More information about creating Jmine Services in Components can be found in the Jmine Services Configuration section below.

Component configuration

A component may need external resources (such as database connections) and behaviour configurations. This configuration should be made explicit through the usage of a SpringComponentConfigurer.

Let’s assume that in the above example we’ll need as configurations a target PrintStream and allow a custom greeting. We may define a configuration bean such as:

package jmine.sample.api;

public interface SampleConfiguration {
        private PrintStream output;
        private String greeting;
        //Getters and setters
}

Then the SpringComponentConfigurer implementation:

package jmine.sample.runtime;

public class SampleConfigurer implements SpringComponentConfigurer {
        private SampleConfiguration sampleConfiguration;
void configure(SpringComponentBuilder builder) {
                builder.addSingleton("output", configuration.getOutput());
        builder.addSingleton("greeting", configuration.getGreeting());
}
//Setter
}

The above code adds the contents of the configuration as singletons, making them available for injection in the service implementation. Other interactions can be made with the SpringComponentBuilder, allowing a wide range of configurations. Below we have the service using the provided beans:

package jmine.sample.runtime;

@Service
public class SampleServiceRuntime implements SampleService {
        @Autowire
        public void SampleServiceRuntime(PrintStream output, @Qualifier("greeting") String greeting) {...

        void sayHello(String toWhom) {
                output.println(greeting + ", " + toWhom);
        }
}

In the component’s runtime xml, we have the following:

<bean class="jmine.tec.component.services.spring.InstallComponentBeanFactoryPostProcessor">
        <property name="componentKey" value="jmine.sample.api.SampleKey"/>
        <property name="componentRoots" value="jmine.sample.runtime.RuntimeRoot"/>
        <property name="configurerClasses" value="jmine.sample.runtime.SampleConfigurer"/>
</bean>

<bean id="sampleConfiguration" class="jmine.sample.api.SampleConfiguration>
        <property name="output" ref="customOutput"/>
        <property name="greeting" ref="${sample.greeting:Hello}"/>
</bean>

This way, we can configure alternative greetings (such as “Ahoy”) in a configuration file, and also provide references to outside objects.

Persistence Configuration

There is a good chance your component uses persistence with Hibernate, in this case you may use the SpringComponentPersistenceConfigurer:

<bean class="jmine.tec.component.services.spring.InstallComponentBeanFactoryPostProcessor">
        <property name="componentKey" value="jmine.sample.api.SampleKey"/>
        <property name="componentRoots" value="jmine.sample.runtime.RuntimeRoot"/>
        <property name="configurerClasses">
                <list>
                        <value>jmine.sample.runtime.SampleConfigurer</value>
                        <value>jmine.tec.persist.impl.services.SpringComponentPersistenceConfigurer</value>
                </list>
        </property>
</bean>

This configurer sets up in your component the necessary data source, Hibernate and an aspect for transactional services (among other goodies):

package jmine.sample.runtime;

@Service
public class SampleServiceRuntime implements SampleService {
        @Autowire
        public void SampleServiceRuntime(Session session, @Qualifier("greeting") String greeting) {...

        @Transactional
        void sayHello(String toWhom) {
                session.save(new Greeting(greeting + ", " + toWhom);
        }
}

Note that, to keep things isolated, each component has it’s own SessionFactory, and each component may have a different DataSource. A JTA (XA) PlatformTransactionManager must be configured to allow for distributed transactions between components and user, if the DataSource is not the same.

Jmine Services Configuration

Jmine Services should be created for a component only as an extension to your component’s API, so that parts of your application that already use Jmine Services can communicate with your component in the same way. Systems that make use of Jmine Services in this way would be Postscript loaders or Integration tests.

Hence, Jmine Services in a component:

  • Must be created in the component’s API module;
  • Must use only its component’s public API to communicate with the component’s runtime implementation.

To create a Jmine Service in your component, the steps are the same as described in the “Criando Serviços” tutorial (in Portuguese):

  • Create a class annotated with @ServiceImplementor
  • Annotate its execution method with @Execution
  • Service inputs should be annotated with @Input or @Parameter as needed
  • Output parameters can be defined with the @Output annotation
  • Dependencies to be injected can be defined using the @Injected annotation.

Services created inside a component’s api module are automatically registered in your application’s ServiceFactory via classpath scanning, and as such don’t need to be registered manually.

Defensive Programming

A Component should be programmed defensively, in order to avoid unwanted side effects, misuse, high coupling and other problems that make maintenance harder. Some defensive tools are given by this module’s structure:

Defensive Package Scanning
Package Scanning is made using a ComponentRoot marked class, and scans only the jar file containing the given class. This avoids classes from other jars with classes in the same package to intrude in your component.
Enforced Runtime jar Scope
The maven-enforcer-plugin is configured in Jmine Tec’s parent pom with a rule that forbids a component’s runtime jar to be declared as dependency in compile or test scopes. This forces the component’s user to import it using the runtime scope, that will be available at runtime but not during compilation. This is an effective way to force the user to use only the API, and never the inner implementation classes.
Wrapping Proxies
All services are exposed to the outside world as proxies that implement only the API, protecting the service’s implementation even from calls using reflection. This way your service may have public setters for dependency injection without worries.

This is a good beginning, but of no use if the API is too open. The API should expose useful services, hiding completely their implementation details, only this way you will be free to evolve and refactor your component.

General Advice

Keep It Simple and Stupid
Avoid unnecessarily complex APIs, if possible keep all arguments and outputs as POJOs. Also keep your implementation as simple as possible.
Single Responsibility Principle
Avoid components that do unrelated things, create more components. Avoid services that do too much, create more services inside a single component.
Dependency Injection
Using Dependency Injection with Spring and annotations is easy, use it! Avoid big classes with too much responsibilities, use dependency injection to share it among several objects. Unit test each object, use integration tests to test the whole component.
YAGNI - Ya Ain’t Gonna Need It
If your user doesn’t need it now, don’t write it.
Avoid Unnecessary Dependencies
Keep your code with as few dependencies as possible. Simple code is easier to understand and maintain.
Avoid Magic
Keep your code as simple and straightforward as possible, avoid invisible magic things that come from nowhere and scare people.
Avoid Mutability
Never expose setters in your services, once created your component must be immutable, all configuration must be done during creation.
Avoid Checked Exceptions
Checked exceptions usually lead to hard to write, read and test code by the API’s user and have bad performance. Use exceptions only for trully exceptional situations, such as system failure and bad usage, always runtime exceptions.
Prefer Composition Over Inheritance
Composition usually leads to a simpler and more maintainable code, avoid inheritance based code reuse.
Keep It Simple and Stupid
Can’t say it enough, really keep it simple.

Classic Component Structure

In the Classic component structure, every project should have an unique package prefix. It’s root may contain a single Controller implementation, and it will be considered the module’s Controller. This commons structure is sometimes used as a name convention to set up some behaviors and implementations.

It is currently used as a very lightweight form of dependency injection, being the sole injected resource for some objects, such as entities. In some modules it also acts as a facade, but this behavior should be avoided, as it tends to create an object with too many responsibilities.

E.g.

jmine.tec.hydra
jmine.tec.hydra.dao
jmine.tec.hydra.domain
jmine.tec.hydra.services

Internationalization

This module defines some basic interfaces, implementations and helpers to create easily testable internationalized messages.

LocalizedMessageHolder represents a single message, that may be retrieved on several locales, isolating message definition from message usage. A MessageCreator creates a message based on several arguments, isolating the code that want’s to create a message from the one that in fact implements how the message is created.

It is usually beneficial to create Java Enums that implement MessageCreator, all pointing to the same bundle and each containing a different bundle key and creating a MessageCreator delegate using MessageCreatorHelper. This enum is easily testable extending AbstractMessageCreatorTest

E.g.

public enum PersistMessages implements MessageCreator {
        DATA_ALREADY_IN_AUTHORIZATION(2, "persist.dataAlreadyInAuthorization");
        private final MessageCreator delegate;
        private PersistMessages(int nargs, String key) {
          this.delegate = MessageCreatorHelper.creator("jmine-tec-persist-messages", key, nargs);
        }
        public LocalizedMessageHolder create(Object... arguments) {
          return this.delegate.create(arguments);
        }
        public int getExpectedArguments() {
          return this.delegate.getExpectedArguments();
        }
}

Exceptions

This module also defines default types for exceptions. All exception types support i18n and receive LocalizedMessageHolders as constructor arguments. See AbstractException, AbstractRuntimeException, AbstractError.

‘Exception throwing guideline‘: throw checked exception only if it is reasonable that the calling code can recover and do something about the problem, throw unchecked instead.

Usually there is not much the system can do about an exception, hence most of them are unchecked. One of the few examples of checked exception is the ubiquitous BeanNotFoundException, thrown then a query that expects to find a single bean finds none.

If an exception message is business related and must be presented to the user, and the user should be responsible for taking actions, a BusinessException must be thrown. This message must be carefully crafted to be easily understandable by the end user and should contain no technical details.

Usually a UserFriendlyExceptionInformer should be used to present exception messages to the end user. The default implementation only shows BusinessException messages.