# JSF Custom Component ValueExpression wird nur als String erkannt



## JackBuuBuu (17. Dez 2009)

Hallo JSF Freunde,

ich habe ein Problem beim Erstellen eines eigenen CommandButton. Das Problem ist wohl die Tag Klasse die wohl nicht erkennt das ein Expression Language Verweis auf eine Beam als Parameter übergeben wird.

Der CommandButton soll anhand eines Keyword und der gesetzten Sprache den Text des Button aus der Datenbank auslesen.

Hier mal der JSF Code:

```
<test:commandButton id="test" action="#{navigationBean.getHome}" keyword="link_home" language="#{localeBean.localeForDb}" />
```

Die Tag Klasse erkennt beim Attribut language die EL nur als String, obwohl language als ValueExpression deklariert ist.

Hier mal der Code der Tag Klasse:

```
package test;

import javax.el.MethodExpression;
import javax.el.ValueExpression;
import javax.faces.component.ActionSource;
import javax.faces.component.ActionSource2;
import javax.faces.component.UIComponent;
import javax.faces.event.MethodExpressionActionListener;
import javax.faces.webapp.UIComponentELTag;

import org.apache.log4j.Logger;

public class CommandButtonTag extends UIComponentELTag {
	
	private Logger logger = Logger.getLogger(CommandButtonTag.class);
	private ValueExpression keyword;
	private ValueExpression language;
	private ValueExpression style;
	private ValueExpression styleClass;
	private MethodExpression actionListener;
	private MethodExpression action;
	
	@Override
	protected void setProperties(UIComponent uiComponent) {
		this.logger.debug("Führe setProperties aus.");
		super.setProperties(uiComponent);
		CommandButton comp = (CommandButton) uiComponent;
		if(this.keyword != null) {
			comp.setValueExpression("keyword", this.keyword);
			this.logger.debug("Parameter keyword auf " + this.keyword.toString() + " gesetzt.");
		}
		if(this.language != null) {
			comp.setValueExpression("language", this.language);
			this.logger.debug("Parameter language auf " + this.language.toString() + " gesetzt.");
		}
		if(this.style != null) {
			comp.setValueExpression("style", this.style);
			this.logger.debug("Parameter style auf " + this.style.toString() + " gesetzt.");
		}
		if(this.styleClass != null) {
			comp.setValueExpression("styleClass", this.styleClass);
			this.logger.debug("Parameter styleClass auf " + this.styleClass.toString() + " gesetzt.");
		}
		if(this.actionListener != null) {
			((ActionSource) comp).addActionListener(new MethodExpressionActionListener(this.actionListener));
			this.logger.debug("Parameter actionListener auf " + this.actionListener.toString() + " gesetzt.");
		}
		if(this.action != null) {
			((ActionSource2) comp).setActionExpression(this.action);
			this.logger.debug("Parameter action auf " + this.action.toString() + " gesetzt.");
		}
	}
	
	@Override
	public void release() {
		this.logger.debug("Führe release aus.");
		super.release();
		this.keyword = null;
		this.language = null;
		this.style = null;
		this.styleClass = null;
		this.action = null;
		this.actionListener = null;
		this.logger.debug("Alle Parameter auf null gesetzt.");
	}

	@Override
	public String getComponentType() {
		return "test.CommandButton";
	}

	@Override
	public String getRendererType() {
		return "test.CommandButton";
	}

	public void setKeyword(ValueExpression keyword) {
		this.keyword = keyword;
	}

	public void setLanguage(ValueExpression language) {
		this.language = language;
	}

	public void setStyle(ValueExpression style) {
		this.style = style;
	}

	public void setStyleClass(ValueExpression styleClass) {
		this.styleClass = styleClass;
	}

	public void setActionListener(MethodExpression actionListener) {
		this.actionListener = actionListener;
	}

	public void setAction(MethodExpression action) {
		this.action = action;
	}
}
```

Und hier bin ich mir sicher ob das so stimmt. Im JSF1.1 musste ich ja noch überprüfen ob es sich um eine ValueReference handelt und ein ValueBinding machen. Laut dem Buch das ich habe langt es wohl bei JSF1.2 das mit dem ValueExpression, so wie im Code dargestellt, zu machen.

Wenn ich die Anwendung im Debug Modus laufen lasse, sehe ich auch das in der ValueExpression die EL als String abgelegt ist. Kann es sein das ich in der Tag Klasse was vergessen habe. Oder vielleicht in der Renderer Klasse?

Hier der Code der Renderer Klasse:

```
package test;

import java.io.IOException;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.event.ActionEvent;
import javax.faces.render.Renderer;

import org.apache.log4j.Logger;

import test.DatabaseController;

public class CommandButtonRenderer extends Renderer {
	
	private Logger logger = Logger.getLogger(CommandButtonRenderer.class);
	
	public void decode(FacesContext facesContext, UIComponent uiComponent) {
		super.decode(facesContext, uiComponent);
		this.logger.debug("Führe decode() aus.");
		uiComponent.queueEvent(new ActionEvent(uiComponent));
	}

	public void encodeBegin(FacesContext facesContext, UIComponent uiComponent) throws IOException {
		ResponseWriter responseWriter = facesContext.getResponseWriter();
		responseWriter.startElement("input", null);
	}
	
	public void encodeEnd(FacesContext facesContext, UIComponent uiComponent) throws IOException {
		this.logger.debug("EncodeEnd wird ausgeführt.");
		CommandButton commandButton = (CommandButton) uiComponent;
		String clientId = commandButton.getClientId(facesContext);
		this.logger.debug("Setze ClientId = " + clientId + ".");
		String keyword = (String) commandButton.getAttributes().get("keyword");
		this.logger.debug("Setze keyword = " + keyword + ".");
		String language = (String) commandButton.getAttributes().get("language");
		this.logger.debug("Setze language = " + language + ".");
		String styleClass = (String) commandButton.getAttributes().get("styleClass");
		this.logger.debug("Setze styleClass = " + styleClass + ".");
		String style = (String) commandButton.getAttributes().get("style");
		this.logger.debug("Setze style = " + style + ".");
		ResponseWriter responseWriter = facesContext.getResponseWriter();
		responseWriter.writeAttribute("type", "submit", null);
		responseWriter.writeAttribute("id", clientId + ":button", "ClientId");
		if(styleClass != null) {
			responseWriter.writeAttribute("class", styleClass, null);
		}
		if(style != null) {
			responseWriter.writeAttribute("style", style, null);
		}
		String text = null;
		if(keyword != null && language != null) {
			DatabaseController dBC = new DatabaseController();
			text = dBC.getTextFromLanguageTable(keyword, language);
			this.logger.debug("Setze text = " + text + ".");
		} else {
			text = "";
			this.logger.debug("Parameter keyword und language sind nicht gesetzt. Parameter text wird auf leeren String gesetzt.");
		}
		responseWriter.writeAttribute("value", text, null);
		responseWriter.endElement("input");
	}
}
```

Hier noch der Code der UIComponent Klasse:

```
package test;

import javax.faces.component.UICommand;
import javax.faces.context.FacesContext;

import org.apache.log4j.Logger;

public class CommandButton extends UICommand {
	
	private Logger logger = Logger.getLogger(CommandButton.class);
	private String keyword;
	private String language;
	private String style;
	private String styleClass;
	
	public Object saveState(FacesContext facesContext) {
		Object values[] = new Object[5];
		values[0] = super.saveState(facesContext);
		values[1] = this.keyword;
		values[2] = this.language;
		values[3] = this.style;
		values[4] = this.styleClass;
		return values;
	}
	
	public void restoreState(FacesContext facesContext, Object state) {
		Object values[] = (Object[]) state;
		super.restoreState(facesContext, values[0]);
		this.keyword = (String) values[1];
		this.language = (String) values[2];
		this.style = (String) values[3];
		this.styleClass = (String) values[4];
	}
	
	public Logger getLogger() {
		return logger;
	}

	public String getKeyword() {
		return keyword;
	}

	public String getLanguage() {
		return language;
	}

	public String getStyle() {
		return style;
	}

	public String getStyleClass() {
		return styleClass;
	}

	public void setLogger(Logger logger) {
		this.logger = logger;
	}

	public void setKeyword(String keyword) {
		this.keyword = keyword;
	}

	public void setLanguage(String language) {
		this.language = language;
	}

	public void setStyle(String style) {
		this.style = style;
	}

	public void setStyleClass(String styleClass) {
		this.styleClass = styleClass;
	}

	public String getFamily() {
		return "javax.faces.Command";
	}
}
```

Hier noch die tld Definition, die Komponentent und Renderer Definition:

```
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
"http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
...
             <tag>
		<description>
			Command Button Internationaler Text. Gibt den Text anhand des Keywords in der gewählten Sparache aus der Datenbank aus.
		</description>
		<name>
			commandButton
		</name>
		<tag-class>
			test.CommandButtonTag
		</tag-class>
		<attribute>
			<name>
				id
			</name>
		</attribute>
		<attribute>
			<name>
				keyword
			</name>
			<deferred-value>
				<type>
					java.lang.String
				</type>
			</deferred-value>
		</attribute>
		<attribute>
			<name>
				language
			</name>
			<deferred-value>
				<type>
					java.lang.String
				</type>
			</deferred-value>
		</attribute>
		<attribute>
			<name>
				style
			</name>
			<deferred-value>
				<type>
					java.lang.String
				</type>
			</deferred-value>
		</attribute>
		<attribute>
			<name>
				styleClass
			</name>
			<deferred-value>
				<type>
					java.lang.String
				</type>
			</deferred-value>
		</attribute>
		<attribute>
			<name>
				action
			</name>
			<deferred-method>
				<method-signature>
					java.lang.Object action()
				</method-signature>
			</deferred-method>
		</attribute>
		<attribute>
			<name>
				actionListener
			</name>
			<deferred-method>
				<method-signature>
					void actionListener(javax.faces.event.ActionEvent)
				</method-signature>
			</deferred-method>
		</attribute>
		<attribute>
			<name>
				rendered
			</name>
		</attribute>
	</tag>
</taglib>



<?xml version="1.0"?>

<!DOCTYPE faces-config PUBLIC
  "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN"
  "http://java.sun.com/dtd/web-facesconfig_1_1.dtd" >

<faces-config>
...	
	<component>
		<component-type>
			test.CommandButton
		</component-type>
		<component-class>
			test.CommandButton
		</component-class>
	</component>
	<render-kit>
		<renderer>
			<component-family>
				javax.faces.Command
			</component-family>
			<renderer-type>
				test.CommandButton
			</renderer-type>
			<renderer-class>
				test.CommandButtonRenderer
			</renderer-class>
		</renderer>
	</render-kit>
</faces-config>
```

Hier die Log4J Ausgabe. Es ist erkennbar das die EL nicht umgesetzt wird, sondern als String übergeben wird:
20091217 15:15:54.202 DEBUG test.CommandButtonRenderer.encodeEnd() Ln49: EncodeEnd wird ausgeführt.
20091217 15:15:54.202 DEBUG test.CommandButtonRenderer.encodeEnd() Ln52: Setze ClientId = j_id_jsp_1853018260_55:test.
20091217 15:15:54.202 DEBUG test.CommandButtonRenderer.encodeEnd() Ln54: Setze keyword = link_home.
20091217 15:15:54.202 DEBUG test.CommandButtonRenderer.encodeEnd() Ln56: Setze language = *#{localeBean.localeForDb}*.
20091217 15:15:54.202 DEBUG test.CommandButtonRenderer.encodeEnd() Ln58: Setze styleClass = null.
20091217 15:15:54.202 DEBUG test.CommandButtonRenderer.encodeEnd() Ln60: Setze style = null.
20091217 15:15:54.218 DEBUG test.DatabaseController.getTextFromLanguageTable() Ln47: SQL Befehl: select *#{localeBean.localeForDb}* from language where keyword = 'link_home';
com.microsoft.sqlserver.jdbc.SQLServerException: Incorrect syntax near '{'.
	at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDatabaseError(Unknown Source)
	at com.microsoft.sqlserver.jdbc.SQLServerStatement.getNextResult(Unknown Source)
...

Es wäre echt klasse wenn ihr mir weiter helfen könnt.


----------



## mmeyer1987 (18. Dez 2009)

Hallo,

Das Problem ist, das du die Werte einfach nur zuweist, ohne zu prüfen, ob es überhaupt ein ValueBinding ist. Hier mal meine Methode(n) aus der Tag-Klasse (Attribut-Namen sind erfunden):


```
public void setProperties(UIComponent component) {
		super.setProperties(component);
		setValueBindingGeneric(component, "id", id);
		setValueBindingGeneric(component, "name", name);
		setValueBindingGeneric(component, "alter",
				alter);
		setValueBindingGeneric(component, "rendered", rendered);
	}

	private void setValueBindingGeneric(UIComponent component,
			String attribute, String value) {
		if (value != null) {
			if (isValueReference(value)) {
				// ValueBindung zuweisen
				setValueBinding(component, attribute, value);
			} else {
				// Wert einfach so zuweisen
				component.getAttributes().put(attribute, value);
			}
		}
	}

private void setValueBinding(UIComponent component, String attribute,
			String value) {
		ValueBinding vb = FacesContext.getCurrentInstance().getApplication()
				.createValueBinding(value);
		component.setValueBinding(attribute, vb);
	}
```

Wichtig ist wie gesagt die Prüfung auf ein ValueBinding. Für weitere Hilfe hier noch ein Link: :rtfm: 

Creating the Component Tag Handler 

Viele Grüße,

Manuel


----------



## mmeyer1987 (18. Dez 2009)

Ach entschuldigung, da war ich zu voreilig :autsch: habe dein Posting wohl nicht richtig gelesen. Habe mit JSF 1.2 noch keine Komponente geschrieben, war mir nicht bewusst, dass man die Prüfung nicht mehr machen muss?! 

Muss ich mal prüfen 

Gruß


----------

