× Home Tutorials Training Consulting Products Books Company Donate Contact us

NOW Hiring

Quick links


OSGi with Eclipse Equinox. This tutorial gives an overview of OSGi and its modularity layer. For this tutorial Eclipse 4.5 (Luna) is used.

1. Introduction into software modularity with OSGi

1.1. What is software modularity?

An application consists of different parts, these are typically called software components or software modules.

These components interact with each other via an Application Programming Interface (API). The API is defined as a set of classes and methods which can be used from other components. A component also has a set of classes and methods which are considered as internal to the software component.

If a component uses an API from another component, it has a dependency to the other component, i.e., it requires the other component exists and works correctly.

A component which is used by other components should try to keep its API stable to avoid that a change affects other components. But it should be free to change its internal implementation.

Java, in its current version (Java 8), provides no structured way to describe software component dependencies. Java only supports the usage of access modifiers, but every public class can be called from another software component. What is desired is a way to explicitly define the API of a software component. The OSGi specification fills this gap.

1.2. The OSGi specification and OSGi implementations

OSGi is a set of specifications which, in its core specification, defines a component and service model for Java. A practical advantage of OSGi is that every software component can define its API via a set of exported Java packages and that every component can specify its required dependencies.

The components and services can be dynamically installed, activated, de-activated, updated and de-installed.

The OSGi specification has several implementations, for example Eclipse Equinox, Knopflerfish OSGi or Apache Felix.

Eclipse Equinox is the reference implementation of the base OSGi specification. It is also the runtime environment on which Eclipse applications are based.

1.3. Plug-in or bundles as software component

The OSGi specification defines a bundle as the smallest unit of modularization, i.e., in OSGi a software component is a bundle. The Eclipse programming model typically calls them plug-in but these terms are interchangeable. A valid plug-in is always a valid bundle and a valid bundle is always a valid plug-in. In this script the usage of plug-in is preferred, to be consistent with the terminology of Eclipse plug-in development.

A plug-in is a cohesive, self-contained unit, which explicitly defines its dependencies to other components and services. It also defines its API via Java packages.

1.4. Naming convention: simple plug-in

A plug-in can be generated by Eclipse via the File ▸ New ▸ Other…​ ▸ Plug-In Development ▸ Plug-In Project menu entry. The corresponding wizard allows specifying several options. This script calls plug-ins generated with the following options a simple plug-in or simple bundle.

  • No Activator

  • No contributions to the user interface

  • Not a rich client application

  • Generated without a template

2. OSGi metadata

2.1. The manifest file (MANIFEST.MF)

Technically OSGi plug-ins are .jar files with additional meta information. This meta information is stored in the META-INF/MANIFEST.MF file. This file is called the manifest file and is part of the standard Java specification and OSGi adds additional metadata to it. According to the Java specification, any Java runtime must ignore unknown metadata. Therefore, plug-ins can be used without restrictions in other Java environments.

The following listing is an example for a manifest file.

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Popup Plug-in
Bundle-SymbolicName: com.example.myosgi; singleton:=true
Bundle-Version: 1.0.0
Bundle-Activator: com.example.myosgi.Activator
Require-Bundle: org.eclipse.ui,
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-1.6

The following table gives an explanation of the identifiers in the manifest file. For information on the version schema which is typically used in OSGi see Semantic Versioning with OSGi.

Table 1. OSGi identifiers in the manifest file
Identifier Description


Short description of the plug-in.


The unique identifier of the plug-in. If this plug-in is using the extension point functionality of Eclipse, it must be marked as Singleton. You do this by adding the following statement after the Bundle-SymbolicName identifier: ; singleton:=true


Defines the plug-in version and must be incremented if a new version of the plug-in is published.


Defines an optional activator class which implements the BundleActivator interface. An instance of this class is created when the plug-in gets activated. Its start() and stop() methods are called whenever the plug-in is started or stopped. An OSGi activator can be used to configure the plug-in during startup. The execution of an activator increases the startup time of the application, therefore this functionality should be used carefully.

Bundle-RequiredExecutionEnvironment (BREE)

Specify which Java version is required to run the plug-in. If this requirement is not fulfilled, then the OSGi runtime does not load the plug-in.


Setting this to lazy will tell the OSGi runtime that this plug-in should only be activated if one of its components, i.e. classes and interfaces are used by other plug-ins. If not set, the Equinox runtime does not activate the plug-in, i.e., services provided by this plug-in are not available.


The Bundle-ClassPath specifies where to load classes from the bundle. The default is '.' which allows classes to be loaded from the root of the bundle. You can also add JAR files to it, these are called nested JAR files.

2.2. Bundle-SymbolicName and Version

Each plug-in has a symbolic name which is defined via the Bundle-SymbolicName property. The name starts by convention with the reverse domain name of the plug-in author, e.g., if you own the "example.com" domain then the symbolic name would start with "com.example".

Each plug-in has also a version number in the Bundle-Version property.

The Bundle-Version and the Bundle-SymbolicName uniquely identifies a plug-in.

2.3. Semantic Versioning with OSGi

OSGi recommends to use a <major>.<minor>.<patch> schema for the version number which is defined via the Bundle-Version field identifier. If you change your plug-in code you increase the version according to the following rule set.

  • <patch> is increased if all changes are backwards compatible.

  • <minor> is increased if public API has changed but all changes are backwards compatible.

  • <major> is increased if changes are not backwards compatible.

For more information on this version scheme see the Eclipse Version Numbering Wiki.

2.4. Specifying plug-in dependencies via the manifest file

A plug-in can define dependencies to other software components via its manifest file. OSGi prevents access to classes without a defined dependency and throws a ClassNotFoundException. The only exception are packages from the Java runtime environment (based on the Bundle-RequiredExecutionEnvironment definition of the plug-in). The JRE packages are always available to a plug-in without an explicitly defined dependency.

If you add a dependency to your manifest file, the Eclipse IDE automatically adds the corresponding JAR file to your project classpath.

You can define dependencies either as plug-in dependencies or package dependencies. If you define a plug-in dependency your plug-in can access all exported packages of this plug-in. If you specify a package dependency you can access this package. Using package dependencies allows you to exchange the plug-in which provides this package at a later point in time. If you require this flexibility prefer the usage of package dependencies.

Dependency management

A plug-in can define that it depends on a certain version (or a range) of another bundle, e.g., plug-in A can define that it depends on plug-in C in version 2.0, while plug-in B defines that it depends on version 1.0 of plug-in C.

The OSGi runtime ensures that all dependencies are present before it starts a plug-in. OSGi reads the manifest file of a plug-in during its installation. It ensures that all dependent plug-ins are also resolved and, if necessary, activates them before the plug-in starts.

2.5. Life cycle of plug-ins in OSGi

With the installation of a plug-in in the OSGi runtime the plug-in is persisted in a local bundle cache. The OSGi runtime then tries to resolve its dependencies.

If all required dependencies are resolved, the plug-in is in the

RESOLVED status otherwise it stays in the INSTALLED status.

In case several plug-ins exist which can satisfy the dependency, the plug-in with the highest valid version is used.

If the versions are the same, the plug-in with the lowest unique identifier (ID) is used. Every plug-in gets this ID assigned by the framework during the installation.

When the plug-in starts, its status is STARTING. After a successful start, it becomes ACTIVE.

This life cycle is depicted in the following graphic.

OSGi life cycle

2.6. Dynamic imports of packages

For legacy reasons OSGi supports a dynamic import of packages. See OSGi Wiki for dynamic imports for details. You should not use this feature, it is a symptom of a non-modular design.

3. API definition of a plug-in

In the MANIFEST.MF file a plug-in also defines its API via the Export-Package Identifier. All packages which are not explicitly exported are not visible to other plug-ins.

Dependency management

All these restrictions are enforced via a specific OSGi classloader. Each plug-in has its own classloader. Access to restricted classes is not possible without using reflection.s

Unfortunately OSGi can not prevent you from using Java reflection to access these classes. This is because OSGi is based on the Java runtime which does not yet support a modularity layer.

Via the x-internal flag the OSGi runtime can mark an exported package as provisional. This allows other plug-ins to consume the corresponding classes, but indicates that these classes are not considered as official API.

The following screenshot shows how to set a package as x-internal in the manifest editor.

Setting the x-internal flag

This is how the corresponding manifest file looks like.

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Provider
Bundle-SymbolicName: de.vogella.osgi.xinternal.provider
Bundle-Version: 1.0.0.qualifier
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Export-Package: de.vogella.osgi.xinternal.provider;x-internal:=true

You can configure how the Eclipse Java editor shows the usage of provisional API. Such an access can be configured to be displayed as, error, warning or if such access should be result in no additional message.

The default is to display a warning message. You can adjust this in the Eclipse preferences via the Window ▸ Preferences ▸ Java ▸ Compiler ▸ Errors/Warnings preference setting.

Settings in Eclipse for warnings for deprecated API usage

You can define that a set of plug-ins can access provisional API without a warning or error message. This can be done via the x-friends directive. This flag is added if you add a plug-in to the Package Visibility section on the Runtime tab of the manifest editor.

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Provider
Bundle-SymbolicName: de.vogella.osgi.xinternal.provider
Bundle-Version: 1.0.0.qualifier
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Export-Package: de.vogella.osgi.xinternal.provider;x-friends:="another.bundle"

The x-friends setting has the same effect as x-internal but all plug-ins mentioned in the x-friends setting can access the package without receiving an error or warning message.

4. Using the OSGi console

4.1. The OSGi console

The OSGi console is like a command-line shell. In this console you can type a command to perform an OSGi action. This can be useful to analyze problems on the OSGi layer of your application.

Use, for example, the command ss to get an overview of all bundles, their status and bundle-id. The following table is a reference of the most important OSGi commands.

Table 2. OSGi commands
Command Description


Lists the available commands.


Lists the installed bundles and their status.

ss vogella

Lists bundles and their status that have vogella within their name.

start <bundle-id>

Starts the bundle with the <bundle-id> ID.

stop <bundle-id>

Stops the bundle with the <bundle-id> ID.

diag <bundle-id>

Diagnoses a particular bundle. It lists all missing dependencies.

install URL

Installs a bundle from a URL.

uninstall <bundle-id>

Uninstalls the bundle with the <bundle-id> ID.

bundle <bundle-id>

Shows information about the bundle with the <bundle-id> ID, including the registered and used services.

headers <bundle-id>

Shows the MANIFST.MF information for a bundle.

services filter

Shows all available services and their consumer. Filter is an optional LDAP filter, e.g., to see all services which provide a ManagedService implementation use the "services (objectclass=*ManagedService)" command.

4.2. Required bundles

You have to add the following bundles to your runtime configuration to use the OSGi console.

  • org.eclipse.equinox.console

  • org.apache.felix.gogo.command

  • org.apache.felix.gogo.runtime

  • org.apache.felix.gogo.shell

4.3. Telnet

If you specify the -console parameter in your launch configuration Eclipse will allow you to interact with the OSGi console. An OSGi launch configuration created with the Eclipse IDE contains this parameter by default. Via the following parameter you can open a port to which you can connect via the telnet protocol.

-console 5555

If you open a telnet session to the OSGi console, you can use tab completion and a history of the commands similar to the Bash shell under Linux.

4.4. Access to the Eclipse OSGi console

You can also access the OSGi console of your running Eclipse IDE. In the Console View you find a menu entry with the tooltip Open Console. If you select Host OSGi Console, you will have access to your running OSGi instance.

Please note that interfering with your running Eclipse IDE via the OSGi console, may put the Eclipse IDE into a bad state.

5. Download the Eclipse SDK

If you plan to add functionalities to the Eclipse platform, you should download the latest Eclipse release. Official releases have stable APIs, therefore are a good foundation for adding your plug-ins and features.

The Eclipse IDE is provided in different flavors. While you can install the necessary tools in any Eclipse package, it is typically easier to download the Eclipse Standard distribution which contains all necessary tools for plug-in development. Other packages adds more tools which are not required for Eclipse plug-in development.

Browse to the Eclipse download site and download the Eclipse Standard package.

Download Eclipse Plug-in IDE
Eclipse 4.5 provides also a new Eclipse installer installer. The installer is useful if you want to download several flavors of Eclipse, as it uses a shared installation pool for common plug-ins.

6. Exercise: Data model plug-in

In this exercise you create a plug-in for the definition of your data model. You also make this data model available to other plug-ins.

6.1. Create the plug-in for the data model

Create a simple plug-in project (see Naming convention: simple plug-in) called com.example.e4.rcp.todo.model.

The following screenshot depicts the second page of the plug-in project wizard and its corresponding settings. Press the Finish button on this page to avoid the usage of templates.

Creating a simple plug-in

6.2. Create the base class

Create the com.example.e4.rcp.todo.model package and the following model class.

package com.example.e4.rcp.todo.model;

import java.util.Date;

public class Todo {

        private final long id;
        private String summary = "";
        private String description = "";
        private boolean done = false;
        private Date dueDate = new Date();

You see an error for your final id field. This error is solved in the next section.

6.3. Generate constructors

Select Source ▸ Generate Constructor using Fields…​ to generate a constructor using all fields. Use the same approach to create another constructor using only the id field.

Ensure that you have created both constructors, because they are required in the following exercises.

6.4. Generate getter and setter methods

Use the Source ▸ Generate Getter and Setter…​ menu to create getters and setters for your fields.

Why is the id field marked as final?

The id is final and therefore Eclipse creates only a getter. This is correct and desired. We will use this field to generate the equals and hashCode() methods therefore it should not be mutable. Changing a field which is used in the equals and hashCode() methods can create bugs which are hard to identify, i.e., an object is contained in a HashMap but not found.

Getter and setter generation

6.5. Adjust the generated getter and setter methods

Adjust the generated getter and setter for the dueDate() field to make defensive copies. The Date class is not immutable and we want to avoid that an instance of Todo can be changed from outside without the corresponding setter.

public Date getDueDate() {
        return new Date(dueDate.getTime());

public void setDueDate(Date dueDate) {
        this.dueDate = new Date(dueDate.getTime());

The resulting class should look like the following listing.

package com.example.e4.rcp.todo.model;

import java.util.Date;

public class Todo {

        private final long id;
        private String summary = "";
        private String description = "";
        private boolean done = false;
        private Date dueDate = new Date();

        public Todo(long id) {
                this.id = id;

        public Todo(long id, String summary, String description, boolean done, Date dueDate) {
                this.id = id;
                this.summary = summary;
                this.description = description;
                this.done = done;


        public long getId() {
                return id;

        public String getSummary() {
                return summary;

        public void setSummary(String summary) {
                this.summary = summary;

        public String getDescription() {
                return description;

        public void setDescription(String description) {
                this.description = description;

        public boolean isDone() {
                return done;

        public void setDone(boolean done) {
                this.done = done;

        public Date getDueDate() {
                return new Date(dueDate.getTime());

        public void setDueDate(Date dueDate) {
                this.dueDate = new Date(dueDate.getTime());


6.6. Generate toString(), hashCode() and equals() methods

Use Eclipse to generate a toString() method for the Todo class based on the id and summary field. This can be done via the Eclipse menu Source ▸ Generate toString()…​.

Also use Eclipse to generate a hashCode() and equals() method based on the id field. This can be done via the Eclipse menu Source ▸ Generate hashCode() and equals()…​.

6.7. Write a copy() method

Add the following copy() method to the class.

public Todo copy() {
        return new Todo(this.id, this.summary,
                        this.description, this.done,

6.8. Create the interface for the todo service

Create the following ITodoService interface.

package com.example.e4.rcp.todo.model;

import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;

public interface ITodoService {

        void getTodos(Consumer<List<Todo>> todosConsumer);

        boolean saveTodo(Todo todo);

        Optional<Todo> getTodo(long id);

        boolean deleteTodo(long id);


6.9. Define the API of the model plug-in

Export the com.example.e4.rcp.todo.model package to define it as API.

For this, open the MANIFEST.MF file and select the Runtime tab. Add com.example.e4.rcp.todo.model to the exported packages.

Exported API

7. Exercise: Create service bundle

In this exercise you create a plug-in for a service implementation which provides access to the data.

This service implementation uses transient data storage, i.e., the data is not persisted between application restarts. To persist the data you could extend this class to store the data for example in a database or the file system. As this storage is not special for Eclipse RCP applications, it is not covered in this script.

7.1. Create a data model provider plug-in (service plug-in)

Create a new simple plug-in (see Naming convention: simple plug-in) project called com.example.e4.rcp.todo.services. This plug-in is called todo service plug-in in the following description.

The MacOS operating system treads folders ending with .service special, therefore we use the .services ending.

7.2. Define the dependencies in the service plug-in

Add the com.example.e4.rcp.todo.model plug-in as dependency to your service plug-in. To achieve this, open the MANIFEST.MF file. Afterwards select the Dependencies tab and add the com.example.e4.rcp.todo.model package to the Imported Packages.

7.3. Provide an implementation of the ITodoService interface

Create the com.example.e4.rcp.todo.services.internal package in your service plug-in and create the following class.

package com.example.e4.rcp.todo.services.internal;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import com.example.e4.rcp.todo.model.ITodoService;
import com.example.e4.rcp.todo.model.Tag;
import com.example.e4.rcp.todo.model.Todo;

public class MyTodoServiceImpl implements ITodoService {

        private static AtomicInteger current = new AtomicInteger(1);
        private List<Todo> todos;

        private Tag<Tag<Todo>> rootTag;

        public MyTodoServiceImpl() {
                todos = createInitialModel();

        public void getTodos(Consumer<List<Todo>> todosConsumer) {
                // always pass a new copy of the data
                todosConsumer.accept(todos.stream().map(t -> t.copy()).collect(Collectors.toList()));

        protected List<Todo> getTodosInternal() {
                return todos;

        // create or update an existing instance of Todo
        public synchronized boolean saveTodo(Todo newTodo) {
                // hold the Optional object as reference to determine, if the Todo is
                // newly created or not
                Optional<Todo> todoOptional = findById(newTodo.getId());

                // get the actual todo or create a new one
                Todo todo = todoOptional.orElse(new Todo(current.getAndIncrement()));

                if (!todoOptional.isPresent()) {
                return true;

        public Optional<Todo> getTodo(long id) {
                return findById(id).map(todo -> todo.copy());

        public boolean deleteTodo(long id) {
                Optional<Todo> deleteTodo = findById(id);

                deleteTodo.ifPresent(todo -> {

                return deleteTodo.isPresent();

        // Example data, change if you like
        private List<Todo> createInitialModel() {
                List<Todo> list = new ArrayList<>();
                list.add(createTodo("Application model", "Flexible and extensible"));
                list.add(createTodo("DI", "@Inject as programming mode"));
                list.add(createTodo("OSGi", "Services"));
                list.add(createTodo("SWT", "Widgets"));
                list.add(createTodo("JFace", "Especially Viewers!"));
                list.add(createTodo("CSS Styling", "Style your application"));
                list.add(createTodo("Eclipse services", "Selection, model, Part"));
                list.add(createTodo("Renderer", "Different UI toolkit"));
                list.add(createTodo("Compatibility Layer", "Run Eclipse 3.x"));
                return list;

        private Todo createTodo(String summary, String description) {
                return new Todo(current.getAndIncrement(), summary, description, false, new Date());

        private Optional<Todo> findById(long id) {
                return getTodosInternal().stream().filter(t -> t.getId() == id).findAny();


7.4. Update the product configuration (via your feature)

Add your new .model and .services plug-ins to your com.example.e4.rcp.todo.feature feature. Ensure that you use the Plug-ins tab on the feature.xml file.

Every time you create a new plug-in and refer to it in your MANIFEST.MF file you have to add it to your product configuration file (via your feature project).

8. Exercise: Create service bundle

8.1. Create a factory to access your service implementation

In [dataservice_plugin] you create two plug-ins. To provide the service implementation to consumers the best solution is to use OSGi services but to keep this description, simple we use a factory in this exercise.

Create a new class called TodoServiceFactory in the com.example.e4.rcp.todo.services package. For this you might need the following tip.

In its default configuration the Eclipse IDE hides parent packages if they don’t contain any classes. During the specification of your class you can define the correct package. This is depicted in the following screenshot.
Specify the package for the creation of a class

The class provides access to your ITodoService implementation via a static method. It can be considered to be a factory for the ITodoService interface. A factory hides the creation of the concrete instance of a certain interface.

package com.example.e4.rcp.todo.services;

import com.example.e4.rcp.todo.model.ITodoService;
import com.example.e4.rcp.todo.services.internal.MyTodoServiceImpl;

 * factory provides access to the todo service provider
public class TodoServiceFactory {

        private static ITodoService todoService = new MyTodoServiceImpl();

        public static ITodoService getInstance() {
                return todoService;


8.2. Export the package in the service plug-in

Export the com.example.e4.rcp.todo.services package via the MANIFEST.MF file on the Runtime tab, so that it is available for other plug-ins.

Please note that the Eclipse tooling does not support the export of empty packages. You have to create at least one class in the package before you are able to export it.

9. Tutorial: Using the Activator and exporting your bundle

In this exercise you create another bundle which uses an Activator. You also run it within Eclipse At the end of this chapter you will also export your bundle to use it later in a stand-alone OSGi server.

9.1. Create a new Bundle

Create a new simple plug-in project "com.vogella.osgi.firstbundle.internal" via File ▸ New ▸ Other…​ ▸ Plug-in Development ▸ Plug-in Project.

9.2. Coding

Create the following thread class.

package com.vogella.osgi.firstbundle.internal;

public class MyThread extends Thread {
        private volatile boolean active = true;

        public void run() {
                while (active) {
                        System.out.println("Hello OSGi console");
                        try {
                        } catch (Exception e) {
                                System.out.println("Thread interrupted " + e.getMessage());

        public void stopThread() {
                active = false;

Change the class Activator.java to the following.

package com.vogella.osgi.firstbundle;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

import de.vogella.osgi.firstbundle.internal.MyThread;

public class Activator implements BundleActivator {
        private MyThread myThread;

        public void start(BundleContext context) throws Exception {
                System.out.println("Starting com.vogella.osgi.firstbundle");
                myThread = new MyThread();

        public void stop(BundleContext context) throws Exception {
                System.out.println("Stopping com.vogella.osgi.firstbundle");


9.3. Run

Select your MANIFEST.MF file, right-click it and select menu:Run As[Run Configuration ]. Create an OSGi Framework launch configuration. deselect all bundles except your de.vogella.osgi.firstbundle. Afterwards press the Add Required bundles. This adds the org.eclipse.osgi bundle to your run configuration.


Run this configuration. Every 5 seconds a new message is written to the console.

In case you are wondering in which folder OSGi starts you find it in ${workspace_loc}/.metadata/plugins/org.eclipse.pde.core/<runconfig>. This folder list the installed bundles via the file "dev.properties" and set the Eclipse workspace as reference in the config.ini file via the osgi.bundles=reference\:file\ statement. This way you can update your bundles in the running OSGi environment directly without any deployment.

9.4. Export your bundle

Export your bundle. This will allow you to install it into a OSGi runtime. Select your bundle and choose File ▸ Export ▸ Plug-in Development ▸ Deployable plug-ins and fragment.


Unflag the option to export the source.


10. Running a stand-alone OSGi server

This chapter will show how to run Equinox as a OSGi stand-alone runtime. In your Eclipse installation directory identify the file org.eclipse.osgi*.jar. This file should be in the "plugin" folder. Copy this jar file to a new place, e.g., c:\temp\osgi-server. Rename the file to "org.eclipse.osgi.jar". Start your OSGi server via the following command.

java -jar org.eclipse.osgi.jar -console

You can use "install URL" to install a bundle from a certain URL. For example to install your bundle from "c:\temp\bundles" use:

install file:c:\temp\bundles\plugins\de.vogella.osgi.firstbundle_1.0.0.jar
You probably need to correct the path and the bundle name on your system.

You can start then the bundle with start and the id.


TIP:You can remove all installed bundles with the -clean parameter.

11. About this website

12. OSGi Resources

12.1. vogella GmbH training and consulting support


The vogella company provides comprehensive training and education services from experts in the areas of Eclipse RCP, Android, Git, Java, Gradle and Spring. We offer both public and inhouse training. Whichever course you decide to take, you are guaranteed to experience what many before you refer to as “The best IT class I have ever attended”.

The vogella company offers expert consulting services, development support and coaching. Our customers range from Fortune 100 corporations to individual developers.

Copyright © 2012-2016 vogella GmbH. Free use of the software examples is granted under the terms of the EPL License. This tutorial is published under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Germany license.

See Licence.