Moab Web Services > Plugins > Plugin Developer's Guide > Utilizing Services or Custom "helper" Classes

Utilizing Services or Custom "Helper" Classes

There are three general types of services available for use in plugins:

These will each be described in this section.

Bundled Services

Bundled services are utility classes that are included and injected by default onto all plugin types. It is not required to use any of these services, but they enable several core features of plugin types as discussed in Utility Services.

More information may be found on each bundled service in Plugin Services.

Using Translators

Often a plugin type class file becomes so complex that it is desirable to split some of its logic into separate utility service classes. The most typical use case for this is to split out the logic for "translating" from a specific resource API to a format of data that the plugin type can natively understand and utilize. For this reason, there is a convention defined to easily add these helper classes called "Translators."

Simply end any class name with "Translator," and it will be automatically injected just as bundled services onto plugin types, other translators, or even custom registered components. The injection occurs only if a field exists on the class matching the name of the translator with the first letter lower-cased. For example, a translator class called "MyTranslator" would be injected on plugin types, other translators, and custom components that define a field called "myTranslator" as def myTranslator or MyTranslator myTranslator.

Do not use two upper-case letters to start the class name of a Translator. Doing this may cause injection to work improperly. For example, use RmTranslator instead of RMTranslator as the class name.

Be careful not to declare translator and custom component injection such that a cyclic dependency is created.

Logging in translators

All translators automatically have a "getLog" method injected on them which can be used to access the configured logger. It returns an instance of org.apache.commons.logging.Log.

package example

class ExampleTranslator {
	public void myMethod() {
		// log will be translated to getLog() by the groovy compiler
		log.info("Starting my method")
	}
}

See Logging for more information on logging configuration and usage.

Example

Suppose that a translator needs to be created to handle a connection to access an external REST resource. The translator could be defined as follows:

package example

class ExampleTranslator {
	public int getExternalNumber() {
		def number = … // Make call to external resource
		return number
	}
}

A plugin type can then use the translator by defining a field called "exampleTranslator". Note that an instance does not need to be explicitly created.

package example

class ExamplePlugin {
	def exampleTranslator
	// OR …
	//ExampleTranslator exampleTranslator

	public void poll() {
		// Use the translator
		log.info("The current number is "+exampleTranslator.getExternalNumber())
	}
}

To extend the example, the translator may also be injected into another translator:

package example

class AnotherTranslator {
	def exampleTranslator

	public int modifyNumber(int number) {
		return number + exampleTranslator.getExternalNumber()
	}
}

This translator may be used in the plugin type just as the other translator.

Registering Custom Components

There are cases where the concept of a "Translator" does not fit the desired use of a utility class. In these cases, it is possible to register any arbitrary class as a component to be injected just as a translator would be. This is done using the Spring Framework's annotation org.springframework.stereotype.Component. When this annotation is used, the class is automatically registered to be injected just as translators onto plugin types and translators.

All annotations are available in the dependencies declared by the plugins-commons artifact.

Do not use two upper-case letters to start the class name of a custom component. Doing this may cause injection to work improperly. For example, use RmUtility instead of RMUtility as the class name.

Changing scope

By default, when a custom component is injected, only a single instance is created for all classes which inject it. This is referred to as the 'singleton' scope. Another scope that is available is 'prototype', which creates a new instance every time it is injected. This is useful when the class contains state data or fields that are modified by multiple methods. To change the scope, use the org.springframework.context.annotation.Scope on the class with a single String parameter specifying "singleton" or "prototype."

Injecting translators or components

The need may arise to inject translators or other custom components onto custom components. This is done using the org.springframework.beans.factory.annotation.Autowired or javax.annotation.Resource annotations. The Autowired annotation is used to inject class instances by the type (i.e. MyTranslator myTranslator) while the Resource annotation is used to inject class instances by the name (i.e. def myTranslator). Add the desired annotation to the field that needs to be injected.

Note that using the Autowired annotation does injection by type which differs from translator and plugin type injection. These are done by name just as the Resource annotation allows. Due to this fact, a type of "def" cannot be used when doing injection onto custom components using the Autowired annotation. See the example below.

Injection of custom components onto translators and plugin types are still done by name, only fields injected using the Autowired annotation are affected.

Be careful not to declare translator and custom component injection such that a cyclic dependency is created.

Logging in custom components

Unlike plugins and translators, custom components do not automatically have a "getLog" method injected on them. In order to log with custom components, you must use the Apache Commons Logging classes to retrieve a new log. The PluginConstants class contains the value of the logger prefix that is used for all plugins and translators. The following is an example of how to retrieve and use a logger correctly in a custom component.

package example

import com.adaptc.mws.plugins.PluginConstants
import org.apache.commons.logging.Log
import org.apache.commons.logging.LogFactory
import org.springframework.stereotype.Component

@Component
class ExampleComponent {
    private static final Log log = LogFactory.getLog(PluginConstants.LOGGER_PREFIX+this.name)

	public void myMethod() {
		log.info("Starting my method")
	}
}

See Logging for more information on logging configuration and usage.

Example

Suppose that a custom utility class is needed to perform complex logic. A custom component could be defined as follows (notice the optional use of the Scope annotation):

package example

import org.springframework.stereotype.Component
import org.springframework.context.annotation.Scope

@Component
@Scope("prototype")
class ComplexLogicHandler {
	def handleLogic() {
		… // Perform complex logic and return
	}
}

A plugin type or translator could then be defined to inject this component:

package example

class CustomPlugin {
	def complexLogicHandler

	public void poll() {
		complexLogicHandler.handleLogic()
	}
}

Now suppose another custom component needs to use the ComplexLogicHandler in its code. It can inject it using the Autowired annotation:

package example

import org.springframework.stereotype.Component
import org.springframework.beans.factory.annotation.Autowired

@Component
class AnotherHandler {
	// Note that this is injected by type, so 'def' may not be used
	@Autowired
	ComplexLogicHandler complexLogicHandler

	def wrapLogic() {
		complexLogicHandler.handleLogic()
	}
}

To perform the same injection but by name (as translators and plugin types are injected), use the Resource annotation:

package example

import org.springframework.stereotype.Component
import javax.annotation.Resource

@Component
class AnotherHandler {
	// Note that this is injected by name based solely on the name defined in
	//	the annotation.  The name of the field itself does not affect the injection.
	@Resource(name="complexLogicHandler")
	def complexLogicHandler

	def wrapLogic() {
		complexLogicHandler.handleLogic()
	}
}

Related Topics 

© 2015 Adaptive Computing