You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by Bernd Fondermann <bf...@brainlounge.de> on 2006/07/12 12:34:33 UTC

Central class for service injection

Having setters to inject service components (Store, DNSServer and all 
the others) into the respective objects creates a new chance to take 
another step to dramatically lower the dependency on Avalon and 
centralize the service lookup code.

A utility class "AvalonServiceInjector" could automatically inject all 
needed services. The manual lookup code would become obsolete.

This yields the chance to remove dependencies on Avalon's ServiceManager 
  from all components.

It further yields the chance to replace the lookup with the use of "your 
favorite ServiceManager".

This is how for example RemoteManager would look now and then:

public void service( final ServiceManager componentManager )
         throws ServiceException {
         super.service(componentManager);

         MailServer mailServer = 
(MailServer)componentManager.lookup(MailServer.ROLE );
         setMailServer(mailServer);
         Store store = (Store)componentManager.lookup( 
org.apache.avalon.cornerstone.services.store.Store" );
         setStore(store);
         UsersStore usersStore = 
(UsersStore)componentManager.lookup(UsersStore.ROLE );
         setUsersStore(usersStore);
         UsersRepository users = 
(UsersRepository)componentManager.lookup(UsersRepository.ROLE);
         if (users == null) {
             throw new ServiceException("","The user repository could 
not be found.");
         }
         setUsers(users);
         SpoolManagementService spoolManagement =
(SpoolManagementService)componentManager.lookup(SpoolManagementService.ROLE);
         setSpoolManagement(spoolManagement);
}

=====>

public void service( final ServiceManager componentManager )
         throws ServiceException {
         super.service(componentManager);

         new AvalonServiceInjector(componentManager).canoncialInject(this);

}


Note: the whole injection line could even be moved out to the caller of 
service() and make service() redundant in most of the cases.

This is how the utility works:

public class AvalonServiceInjector extends ServiceInjector {

     private ServiceManager serviceManager;

     public AvalonServiceInjector(ServiceManager serviceManager) {
         this.serviceManager = serviceManager;
     }

     protected Object lookup(String typeFQName) {
         try {
             return serviceManager.lookup(typeFQName);
         } catch (ServiceException e) {
             return null;
         }
     }
}

Note: the lookup could easily be replaced by JNDI lookups or anything else.

The beef happens to happen in all-purpose ServiceInjector which is 
already totally independent of Avalon. It uses reflection to gather all 
setters and tries to find fitting objects in ServiceManager:


public abstract class ServiceInjector {

   protected abstract Object lookup(String fullyQualifiedType);

   public int canoncialInject(Object injectionReceiver) {

     Class injecteeClass = injectionReceiver.getClass();
     Method[] methods = injecteeClass.getMethods();
     int injectionCount = 0;

     for (int i = 0; i < methods.length; i++) {

       Method method = methods[i];
       String methodName = method.getName();
       if (!methodName.startsWith("set")) continue; // is it a setter?

       Class[] parameterTypes = method.getParameterTypes();
       // is it a one parameter setter?
       if (parameterTypes == null || parameterTypes.length != 1) continue;

       Class parameterType = parameterTypes[0];
       String injectionClassCandidate = methodName.substring(3);

       // does setter parameter match the setter name?
       String typeFQName = parameterType.getName();
       if (!typeFQName.endsWith(injectionClassCandidate)) {
         System.out.println("setter name/param mismatch: " + 
injectionClassCandidate + " <> " + parameterType);
         continue;
       }

       Object injected = lookup(typeFQName);
       if (injected == null) {
         System.out.println("setter parameter lookup failed: " + 
typeFQName);
         continue;
       }

       try {
         method.invoke(injectionReceiver, new Object[] {injected});
         System.out.println("setter injection successfull: " + 
injectionClassCandidate);
         injectionCount++;
       } catch (IllegalAccessException e) {
         e.printStackTrace();
       } catch (InvocationTargetException e) {
         e.printStackTrace();
       }
     }
     return injectionCount;
   }

}

This works perfectly after renaming those setters not already strictly 
following the setter naming convention.

By evaluating the canoncialInject() return code the component has full 
control to check whether all of its components have been injected.

   Bernd



---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


Re: Central class for service injection

Posted by Bernd Fondermann <bf...@brainlounge.de>.
Stefano Bagnara wrote:
> Bernd Fondermann wrote:
> 
>> Having setters to inject service components (Store, DNSServer and all 
>> the others) into the respective objects creates a new chance to take 
>> another step to dramatically lower the dependency on Avalon and 
>> centralize the service lookup code.
> 
> 
> You're almost precisely describing my ideas ;-) thanks!
> Differences in preferences follow:
> 
>> A utility class "AvalonServiceInjector" could automatically inject all 
>> needed services. The manual lookup code would become obsolete.
> 
> 
> I would call it ContainerUtil (ala Avalon) so we can use it later for 
> lifecycle management and more.

I'd call a ServiceInjector a "ServiceInjector" ;-) But I have no problem 
to encapsulate instantiation + call in a Lifecycle utility class.

> Right. We should remove the whole service method and put the injector 
> util in a component wrapper (or in the mailet/matcher loader for mailets 
> and handlerchain for handlers).

OK.

> I would prefer Enabling interfaces over setter reflection in order to 
> use AutoWiring because it is more selfdocumenting.
> This comes at the cost of an additional interface for every service but 
> it allow the developer to declare that a specific setter is there to 
> satisfy a dependency.
> 
> e.g: We have UsersRepository, we add a UsersRepositoryAware interfaces:
> interface UsersRepositoryAware {
> void setUsersRepository(UsersRepository ur);
> }
> then when you write a component that need this service you write the 
> setter (as you already did for your proposal) and you also add 
> "implements UsersRepositoryAware".

OK, I see the advantage that it is more verbose. But has the downside 
that you have
a. to declare the interface
b. add implements clause where service is used
c. add injection code, probably resulting in a copy/paste-like stanza 
for injecting

but I agree that verbosity here may be more important.

we could still use reflection if we used the convention, that such 
interfaces have to be named for example UsersRepositoryServiceAware and 
we could add a "interface.endswith("ServiceAware")"-check to my proposed 
code.

> 
> Furthermore I want to add that autowiring (either by setter reflection 
> or enabling interfaces) is a cool thing but it is also an obfuscator and 
> it sometimes limit your flexibility. A fix to this problem is to rely on 
> super-container declarations (see assembly.xml and avalon 
> ServiceManager) or to provide our own way to declare "service roles".

As far as I can see, this "flexibility" Avalon/Phoenix provides is not 
used anywhere right now. So the flexibility with all the roles, xinfo, 
assembly.xml becomes a bunch of over-verbose, redundant configurations 
where simply class-/interface-names are repeated all over.

> Often, when setter injection (with or without enabling interfaces) is 
> used a serviced() lifecycle method is added to give to the object the 
> opportunity to verify the dependencies we received and start its own 
> "work". Maybe we can do this at the beginning of another already 
> existing lifecycle.

+1

   Bernd

---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


Re: Central class for service injection

Posted by Stefano Bagnara <ap...@bago.org>.
Bernd Fondermann wrote:
> Having setters to inject service components (Store, DNSServer and all 
> the others) into the respective objects creates a new chance to take 
> another step to dramatically lower the dependency on Avalon and 
> centralize the service lookup code.

You're almost precisely describing my ideas ;-) thanks!
Differences in preferences follow:

> A utility class "AvalonServiceInjector" could automatically inject all 
> needed services. The manual lookup code would become obsolete.

I would call it ContainerUtil (ala Avalon) so we can use it later for 
lifecycle management and more.

> This yields the chance to remove dependencies on Avalon's ServiceManager 
>  from all components.
> [...
> 
> public void service( final ServiceManager componentManager )
>         throws ServiceException {
>         super.service(componentManager);
> 
>         new AvalonServiceInjector(componentManager).canoncialInject(this);
> 
> }
> 
> 
> Note: the whole injection line could even be moved out to the caller of 
> service() and make service() redundant in most of the cases.

Right. We should remove the whole service method and put the injector 
util in a component wrapper (or in the mailet/matcher loader for mailets 
and handlerchain for handlers).

> This is how the utility works:
> [...
> The beef happens to happen in all-purpose ServiceInjector which is 
> already totally independent of Avalon. It uses reflection to gather all 
> setters and tries to find fitting objects in ServiceManager:
> [...

I would prefer Enabling interfaces over setter reflection in order to 
use AutoWiring because it is more selfdocumenting.
This comes at the cost of an additional interface for every service but 
it allow the developer to declare that a specific setter is there to 
satisfy a dependency.

e.g: We have UsersRepository, we add a UsersRepositoryAware interfaces:
interface UsersRepositoryAware {
void setUsersRepository(UsersRepository ur);
}
then when you write a component that need this service you write the 
setter (as you already did for your proposal) and you also add 
"implements UsersRepositoryAware".

Furthermore I want to add that autowiring (either by setter reflection 
or enabling interfaces) is a cool thing but it is also an obfuscator and 
it sometimes limit your flexibility. A fix to this problem is to rely on 
super-container declarations (see assembly.xml and avalon 
ServiceManager) or to provide our own way to declare "service roles".

> This works perfectly after renaming those setters not already strictly 
> following the setter naming convention.
> 
> By evaluating the canoncialInject() return code the component has full 
> control to check whether all of its components have been injected.
> 
>   Bernd

Often, when setter injection (with or without enabling interfaces) is 
used a serviced() lifecycle method is added to give to the object the 
opportunity to verify the dependencies we received and start its own 
"work". Maybe we can do this at the beginning of another already 
existing lifecycle.

Stefano


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


Re: Central class for service injection

Posted by Bernd Fondermann <bf...@brainlounge.de>.
You don't even have 10 minutes?? ;-)

Of course, I don't mind if you use it.

   Bernd


Siegfried Goeschl wrote:
> Hi Bernd,
> 
> do you mind if I nick the idea for Turbine/Fulcrum ... :-) ... never had 
> the time to write it.
> 
> Cheers,
> 
> Siegfried Goeschl
> 
> Bernd Fondermann wrote:
> 
>> Having setters to inject service components (Store, DNSServer and all 
>> the others) into the respective objects creates a new chance to take 
>> another step to dramatically lower the dependency on Avalon and 
>> centralize the service lookup code.
>>
>> A utility class "AvalonServiceInjector" could automatically inject all 
>> needed services. The manual lookup code would become obsolete.
>>
>> This yields the chance to remove dependencies on Avalon's 
>> ServiceManager  from all components.
>>
>> It further yields the chance to replace the lookup with the use of 
>> "your favorite ServiceManager".
>>
>> This is how for example RemoteManager would look now and then:
>>
>> public void service( final ServiceManager componentManager )
>>         throws ServiceException {
>>         super.service(componentManager);
>>
>>         MailServer mailServer = 
>> (MailServer)componentManager.lookup(MailServer.ROLE );
>>         setMailServer(mailServer);
>>         Store store = (Store)componentManager.lookup( 
>> org.apache.avalon.cornerstone.services.store.Store" );
>>         setStore(store);
>>         UsersStore usersStore = 
>> (UsersStore)componentManager.lookup(UsersStore.ROLE );
>>         setUsersStore(usersStore);
>>         UsersRepository users = 
>> (UsersRepository)componentManager.lookup(UsersRepository.ROLE);
>>         if (users == null) {
>>             throw new ServiceException("","The user repository could 
>> not be found.");
>>         }
>>         setUsers(users);
>>         SpoolManagementService spoolManagement =
>> (SpoolManagementService)componentManager.lookup(SpoolManagementService.ROLE); 
>>
>>         setSpoolManagement(spoolManagement);
>> }
>>
>> =====>
>>
>> public void service( final ServiceManager componentManager )
>>         throws ServiceException {
>>         super.service(componentManager);
>>
>>         new 
>> AvalonServiceInjector(componentManager).canoncialInject(this);
>>
>> }
>>
>>
>> Note: the whole injection line could even be moved out to the caller 
>> of service() and make service() redundant in most of the cases.
>>
>> This is how the utility works:
>>
>> public class AvalonServiceInjector extends ServiceInjector {
>>
>>     private ServiceManager serviceManager;
>>
>>     public AvalonServiceInjector(ServiceManager serviceManager) {
>>         this.serviceManager = serviceManager;
>>     }
>>
>>     protected Object lookup(String typeFQName) {
>>         try {
>>             return serviceManager.lookup(typeFQName);
>>         } catch (ServiceException e) {
>>             return null;
>>         }
>>     }
>> }
>>
>> Note: the lookup could easily be replaced by JNDI lookups or anything 
>> else.
>>
>> The beef happens to happen in all-purpose ServiceInjector which is 
>> already totally independent of Avalon. It uses reflection to gather 
>> all setters and tries to find fitting objects in ServiceManager:
>>
>>
>> public abstract class ServiceInjector {
>>
>>   protected abstract Object lookup(String fullyQualifiedType);
>>
>>   public int canoncialInject(Object injectionReceiver) {
>>
>>     Class injecteeClass = injectionReceiver.getClass();
>>     Method[] methods = injecteeClass.getMethods();
>>     int injectionCount = 0;
>>
>>     for (int i = 0; i < methods.length; i++) {
>>
>>       Method method = methods[i];
>>       String methodName = method.getName();
>>       if (!methodName.startsWith("set")) continue; // is it a setter?
>>
>>       Class[] parameterTypes = method.getParameterTypes();
>>       // is it a one parameter setter?
>>       if (parameterTypes == null || parameterTypes.length != 1) continue;
>>
>>       Class parameterType = parameterTypes[0];
>>       String injectionClassCandidate = methodName.substring(3);
>>
>>       // does setter parameter match the setter name?
>>       String typeFQName = parameterType.getName();
>>       if (!typeFQName.endsWith(injectionClassCandidate)) {
>>         System.out.println("setter name/param mismatch: " + 
>> injectionClassCandidate + " <> " + parameterType);
>>         continue;
>>       }
>>
>>       Object injected = lookup(typeFQName);
>>       if (injected == null) {
>>         System.out.println("setter parameter lookup failed: " + 
>> typeFQName);
>>         continue;
>>       }
>>
>>       try {
>>         method.invoke(injectionReceiver, new Object[] {injected});
>>         System.out.println("setter injection successfull: " + 
>> injectionClassCandidate);
>>         injectionCount++;
>>       } catch (IllegalAccessException e) {
>>         e.printStackTrace();
>>       } catch (InvocationTargetException e) {
>>         e.printStackTrace();
>>       }
>>     }
>>     return injectionCount;
>>   }
>>
>> }
>>
>> This works perfectly after renaming those setters not already strictly 
>> following the setter naming convention.
>>
>> By evaluating the canoncialInject() return code the component has full 
>> control to check whether all of its components have been injected.
>>
>>   Bernd
>>
>>
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
>> For additional commands, e-mail: server-dev-help@james.apache.org
>>
>>
>>
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
> For additional commands, e-mail: server-dev-help@james.apache.org
> 
> 


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


Re: Central class for service injection

Posted by Siegfried Goeschl <si...@it20one.at>.
Hi Bernd,

do you mind if I nick the idea for Turbine/Fulcrum ... :-) ... never had 
the time to write it.

Cheers,

Siegfried Goeschl

Bernd Fondermann wrote:
> Having setters to inject service components (Store, DNSServer and all 
> the others) into the respective objects creates a new chance to take 
> another step to dramatically lower the dependency on Avalon and 
> centralize the service lookup code.
> 
> A utility class "AvalonServiceInjector" could automatically inject all 
> needed services. The manual lookup code would become obsolete.
> 
> This yields the chance to remove dependencies on Avalon's ServiceManager 
>  from all components.
> 
> It further yields the chance to replace the lookup with the use of "your 
> favorite ServiceManager".
> 
> This is how for example RemoteManager would look now and then:
> 
> public void service( final ServiceManager componentManager )
>         throws ServiceException {
>         super.service(componentManager);
> 
>         MailServer mailServer = 
> (MailServer)componentManager.lookup(MailServer.ROLE );
>         setMailServer(mailServer);
>         Store store = (Store)componentManager.lookup( 
> org.apache.avalon.cornerstone.services.store.Store" );
>         setStore(store);
>         UsersStore usersStore = 
> (UsersStore)componentManager.lookup(UsersStore.ROLE );
>         setUsersStore(usersStore);
>         UsersRepository users = 
> (UsersRepository)componentManager.lookup(UsersRepository.ROLE);
>         if (users == null) {
>             throw new ServiceException("","The user repository could not 
> be found.");
>         }
>         setUsers(users);
>         SpoolManagementService spoolManagement =
> (SpoolManagementService)componentManager.lookup(SpoolManagementService.ROLE); 
> 
>         setSpoolManagement(spoolManagement);
> }
> 
> =====>
> 
> public void service( final ServiceManager componentManager )
>         throws ServiceException {
>         super.service(componentManager);
> 
>         new AvalonServiceInjector(componentManager).canoncialInject(this);
> 
> }
> 
> 
> Note: the whole injection line could even be moved out to the caller of 
> service() and make service() redundant in most of the cases.
> 
> This is how the utility works:
> 
> public class AvalonServiceInjector extends ServiceInjector {
> 
>     private ServiceManager serviceManager;
> 
>     public AvalonServiceInjector(ServiceManager serviceManager) {
>         this.serviceManager = serviceManager;
>     }
> 
>     protected Object lookup(String typeFQName) {
>         try {
>             return serviceManager.lookup(typeFQName);
>         } catch (ServiceException e) {
>             return null;
>         }
>     }
> }
> 
> Note: the lookup could easily be replaced by JNDI lookups or anything else.
> 
> The beef happens to happen in all-purpose ServiceInjector which is 
> already totally independent of Avalon. It uses reflection to gather all 
> setters and tries to find fitting objects in ServiceManager:
> 
> 
> public abstract class ServiceInjector {
> 
>   protected abstract Object lookup(String fullyQualifiedType);
> 
>   public int canoncialInject(Object injectionReceiver) {
> 
>     Class injecteeClass = injectionReceiver.getClass();
>     Method[] methods = injecteeClass.getMethods();
>     int injectionCount = 0;
> 
>     for (int i = 0; i < methods.length; i++) {
> 
>       Method method = methods[i];
>       String methodName = method.getName();
>       if (!methodName.startsWith("set")) continue; // is it a setter?
> 
>       Class[] parameterTypes = method.getParameterTypes();
>       // is it a one parameter setter?
>       if (parameterTypes == null || parameterTypes.length != 1) continue;
> 
>       Class parameterType = parameterTypes[0];
>       String injectionClassCandidate = methodName.substring(3);
> 
>       // does setter parameter match the setter name?
>       String typeFQName = parameterType.getName();
>       if (!typeFQName.endsWith(injectionClassCandidate)) {
>         System.out.println("setter name/param mismatch: " + 
> injectionClassCandidate + " <> " + parameterType);
>         continue;
>       }
> 
>       Object injected = lookup(typeFQName);
>       if (injected == null) {
>         System.out.println("setter parameter lookup failed: " + 
> typeFQName);
>         continue;
>       }
> 
>       try {
>         method.invoke(injectionReceiver, new Object[] {injected});
>         System.out.println("setter injection successfull: " + 
> injectionClassCandidate);
>         injectionCount++;
>       } catch (IllegalAccessException e) {
>         e.printStackTrace();
>       } catch (InvocationTargetException e) {
>         e.printStackTrace();
>       }
>     }
>     return injectionCount;
>   }
> 
> }
> 
> This works perfectly after renaming those setters not already strictly 
> following the setter naming convention.
> 
> By evaluating the canoncialInject() return code the component has full 
> control to check whether all of its components have been injected.
> 
>   Bernd
> 
> 
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
> For additional commands, e-mail: server-dev-help@james.apache.org
> 
> 
> 

---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org