# [EJB] Dependency Injection



## Orolhawion (18. Mai 2011)

Hallo zusammen,

ich arbeite mich gerade in EJB3 ein und es tauchen gelegentlich Fragen auf, die ich mir derzeit noch nicht beantworten kann.

Folgendes Szenario:

Ich habe eine Enterprise Application mit EJB3 auf einem JBoss AS 6 laufen. Parallel dazu läuft die gleiche Enterprise Application mit EJB3 auf einem entfernten Rechner, ebenfalls unter JBoss AS6. Ich möchte nun mit meinem Client, der remote auf noch einer anderen Maschine läuft, die EJBs benutzen, was soweit auch erstmal klappt. Auf den Remote Client bekomme ich zu den EJBs die passenden Proxy-Objekte mittels JNDI-Lookup. Die JNDI Properties liegen in der jndi.properties-Datei im src-Folder. Dort gibt es das Attribut:

```
java.naming.provider.url=jnp://localhost:1099
```
Angenommen ich habe eine wie oben beschriebene verteilte Anwendung und möchte sowohl von Server1 als auch von Server2 Beans in meinem Remote Client benutzen. Und obendrein weiß ich nicht welche Bean auf welchem der beiden Server zu finden ist.
Ich habe folgendes versucht:

```
// jndi.properties
java.naming.provider.url=jnp://server1:1099,jnp://server2:1099
```


```
public Object getBeanByJNDILookup(String jndiName) {
	Object result = null; // any bean fits into an Object (cast in receiving method required)
	try {
		String properties = (String) this.getContext().getEnvironment().get("java.naming.provider.url");
		String[] namingURLs = properties.split(",");
		for (int i=0; i<namingURLs.length; i++) {
			try {
				this.getContext().addToEnvironment("java.naming.provider.url", namingURLs[i]);
				result = this.getContext().lookup(jndiName);
				i = namingURLs.length;
			} catch (NamingException e) {
				System.err.println(this.getClass().getSimpleName() + " says: " + e.getMessage() + " (Host: " + namingURLs[i] + ")");
				this.setContext(null);
			}
		}
	} catch (NamingException e1) {
		System.err.println(this.getClass().getSimpleName() + " says: " + e1.getMessage());
	}
	// make sure that the original context is loaded next time
	this.setContext(null);
	return result;
}
```
Das funktioniert auch. Wird die Bean bei Server 1 nicht gefunden, wird bei Server 2 angefragt.

Erstens die Frage, hab ich da das Rad neu erfunden und bringt EJB so etwas schon mit (habe nichts gefunden, daher dieser Versuch)?

Zweitens: folgendes Szenario: Server1 und Server2 beherbergen beide jeweils eine EJB die sich gegenseitig benutzen sollen. Das klappt mittels DependencyInjection. Ich annotiere auf Server1 wie folgt:

```
@EJB
private MyBeanRemote myBeanFromServer2;
```
auf Server2 wie folgt:

```
@EJB
private MyBeanRemote myBeanFromServer1;
```
In den JNDI-Properties habe ich als java.naming.provider.url jeweils die Adresse des anderen Servers angegeben. Klappt auch alles, aber was mache ich denn, wenn ich nicht weiß auf welchem der beiden Server die gesuchte Bean bereitgestellt wird? Ich würde gerne annotieren wie oben beschrieben, und der Interceptor soll dann rausfinden woher er die Bean bekommt, ähnlich des JNDI-Lookup, den ich oben beschrieb...

Weiß jemand Rat?


----------



## FArt (19. Mai 2011)

Im Prinzip hast du es so einigermaßen richtig gemacht. Wichtig ist, dass du die Properties nur in dem Context vom Default änderst, was du ja anscheinend gemacht hast.

Wie geht es besser?
Natürlich hast du das Rad so teilweise neu erfunden. Im Prinzip bilden deine beiden Server einen Cluster. JBoss kommt mit voll ausgebildeteten Clusterfunktionalitäten daher. In einem Cluster, kannst du dich einfach so verhalten, wie in einer Instanz. Um Lookup (HA-JNDI) kümmert sich der Server, auch wo letztendlich das Bean zu finden ist.

Dein Weg ist ok, wenn du unter allen Umständen einen Cluster vermeiden möchtest. ich finde jedoch, ein einfacher Cluster ist leichter zu administrieren als eine Applikation mit obigem Verhalten (wenn es etwas komlexer wird und wir nicht nur von einem Bean reden).


----------



## Orolhawion (19. Mai 2011)

bedeutet das jetzt, dass ich ohne cluster immer einen JNDI lookup machen muss und dependency injection mittels annotation nicht funktionieren wird? immerhin funktioniert das ja wenn ein server nicht antwortet, dann wird halt auf dem nächsten server nachgesehen (mit annotation). Dummerweise ist "bean not bound" auch eine antwort, was dann dazu führt, dass eben nicht mehr beim nächsten server nach der bean gefragt wird...


----------



## FArt (19. Mai 2011)

Orolhawion hat gesagt.:


> bedeutet das jetzt, dass ich ohne cluster immer einen JNDI lookup machen muss und dependency injection mittels annotation nicht funktionieren wird? immerhin funktioniert das ja wenn ein server nicht antwortet, dann wird halt auf dem nächsten server nachgesehen (mit annotation). Dummerweise ist "bean not bound" auch eine antwort, was dann dazu führt, dass eben nicht mehr beim nächsten server nach der bean gefragt wird...



An der Stelle bin ich mir nicht sicher, ob das so spezifiziert ist oder (was ich eher glaube) sich der JBoss halt zufällig so verhält und sich dieses Verhalten durchaus auch in Zukunft ändern kann.

Wenn du es nach Spezifikation machen möchtest, dann bist du mit einem Cluster auf der sicheren Seite. Sonst würde ich hier von Annotationen absehen und den Zugriff über einen expliziten Lookup regeln.

Die Lösung hängt eben davon ab, ob das oft gemacht werden muss oder nicht. Je komplexer die Verflechtungen sind, desto eher sollte man einen Cluster in Betracht ziehen. Wie gesagt, einfaches Clustering (kein Failover, keine Lastverteilung, nur verteilte Logik) ist relativ einfach zu konfigurieren. Im Prinzip ist es dann nur HA-JNDI.


----------



## Orolhawion (19. Mai 2011)

ok, dann gehen wir jetzt mal davon aus, dass ein cluster nicht in frage kommt. ich habe im jboss forum einen beitrag gefunden, der genauer beschreibt was ich eigentlich möchte, allerdings nicht beantwortet wie das geht (How to inject EJBs with custom injector? | EJB3 | JBoss Community) ich müsste also eigentlich nur das standardverhalten der @EJB annotation mit dem verhalten meiner methode ersetzen und schon hätte ich erreicht was ich möchte. gesucht ist ein objekt, dem ich verschiedene jndi-serveradressen geben kann bei denen eine von mir spezifizierte bean gesucht wird. mich wundert ehrlich gesagt, dass dieser wunsch so wahnsinnig speziell sein soll.. :-/


----------



## FArt (19. Mai 2011)

Orolhawion hat gesagt.:


> ok, dann gehen wir jetzt mal davon aus, dass ein cluster nicht in frage kommt. ich habe im jboss forum einen beitrag gefunden, der genauer beschreibt was ich eigentlich möchte, allerdings nicht beantwortet wie das geht (How to inject EJBs with custom injector? | EJB3 | JBoss Community) ich müsste also eigentlich nur das standardverhalten der @EJB annotation mit dem verhalten meiner methode ersetzen und schon hätte ich erreicht was ich möchte. gesucht ist ein objekt, dem ich verschiedene jndi-serveradressen geben kann bei denen eine von mir spezifizierte bean gesucht wird. mich wundert ehrlich gesagt, dass dieser wunsch so wahnsinnig speziell sein soll.. :-/



Es gibt keinen Grund, den offiziellen Weg nicht zu beschreiten. Das Argument mit dem transaktionellen Verhalten aus obigem Post finde ich absolut schlüssig. Ich nehme an, wenn man die Spec bzgl. dem injekten des EJB liest, wird man darauf stoßen, dass das so nicht vorgesehen ist, da sich das Verhalten ja ändert, je nachdem wo das Bean physikalisch liegt. Ein Entwickler erwartet z.B. beim Transaktionsattribut "required", dass eine bestehende Transaktion auch in dem gerufenen Bean zieht.
Das mag für deinen Anwendungsfall irrelevant sein, für einen allgemeinen Mechanismus aber schon.

In einem Cluster würde die Transaktion als verteilte Transaktion ausgeführt werden, also absolut konsistent.

Ich gebe ungern Tipps für (miese) Workarounds, denn diese tendieren einem sehr schnell wieder auf die Füße zu fallen. Dann wiederhole ich lieber meinen Tipp von vorhin: mache es konsistent mit einem Cluster oder mit einem expliziten JNDI Lookup. Beim expliziten Lookup sieht jeder, dass man hier die Grenzen des AS (Transaktion, Security, ...) verlässt bzw. explizit konfigurieren muss, nämlich als wäre man ein "normaler" Client zu der anderen AS Instanz.


----------



## Orolhawion (19. Mai 2011)

ok, das problem ist offenbar nicht gelöst. auf anfrage kam folgende nachricht zurück:

Re: How to inject EJBs with custom injector? | EJB3 | JBoss Community


> > Hi,
> > I didn't solve it. Seems like it's not supposed to be replaced by a costum behavior. We are still calling the service locator within the consumer. Introducing a new annotation could help you. But we thought it's not worth, because we wanted to replace the default behavior transparently and that's not possible.
> 
> 
> hi, have you been able to solve the problem? i created almost the same thing that you have and now would like replace the default behaviour with my own...





> Beim expliziten Lookup sieht jeder, dass man hier die Grenzen des AS (Transaktion, Security, ...) verlässt bzw. explizit konfigurieren muss, nämlich als wäre man ein "normaler" Client zu der anderen AS Instanz.


das erscheint mir in der tat sinnvoll. nunja. vielen dank für deine antworten.


----------



## Orolhawion (25. Mai 2011)

Ich habe nun eine eigene Annotation und einen eigenen Interceptor entwickelt. Der Interceptor greift beim @PostConstruct ein und führt den obigen Algorithmus aus, um die Bean zu finden und injiziert sie dann, falls sie gefunden wird. War gar nicht so schwer.  Jetzt brauch ich nur noch meine eigene Annotation zu benutzen und schwups ist mir egal auf welchem Server die Bean liegt und ob ich nen Cluster habe oder nicht.


----------

