/*
@ITMillApache2LicenseForJavaFiles@
*/
package com.vaadin.ui;
import java.io.ByteArrayInputStream;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import com.vaadin.Application;
import com.vaadin.terminal.ApplicationResource;
import com.vaadin.terminal.DownloadStream;
import com.vaadin.terminal.ParameterHandler;
import com.vaadin.terminal.URIHandler;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
/**
* LoginForm is a Vaadin component to handle common problem among Ajax
* applications: browsers password managers don't fill dynamically created forms
* like all those UI elements created by Vaadin.
*
* For developer it is easy to use: add component to a desired place in you UI
* and add LoginListener to validate form input. Behind the curtain LoginForm
* creates an iframe with static html that browsers detect.
*
* Login form is by default 100% width and height, so consider using it inside a
* sized {@link Panel} or {@link Window}.
*
* Login page html can be overridden by replacing protected getLoginHTML method.
* As the login page is actually an iframe, styles must be handled manually. By
* default component tries to guess the right place for theme css.
*
* Note, this is a new Ajax terminal specific component and is likely to change.
*
* @since 5.3
*/
public class LoginForm extends CustomComponent {
private Embedded iframe = new Embedded();
private ApplicationResource loginPage = new ApplicationResource() {
public Application getApplication() {
return LoginForm.this.getApplication();
}
public int getBufferSize() {
return getLoginHTML().length;
}
public long getCacheTime() {
return -1;
}
public String getFilename() {
return "login";
}
public DownloadStream getStream() {
return new DownloadStream(new ByteArrayInputStream(getLoginHTML()),
getMIMEType(), getFilename());
}
public String getMIMEType() {
return "text/html; charset=utf-8";
}
};
private ParameterHandler paramHandler = new ParameterHandler() {
public void handleParameters(Map parameters) {
if (parameters.containsKey("username")) {
getWindow().addURIHandler(uriHandler);
HashMap params = new HashMap();
// expecting single params
for (Iterator it = parameters.keySet().iterator(); it
.hasNext();) {
String key = it.next();
String value = (parameters.get(key))[0];
params.put(key, value);
}
LoginEvent event = new LoginEvent(params);
fireEvent(event);
}
}
};
private URIHandler uriHandler = new URIHandler() {
private final String responce = "Login form handeled."
+ "";
public DownloadStream handleURI(URL context, String relativeUri) {
if (relativeUri != null && relativeUri.contains("loginHandler")) {
if (window != null) {
window.removeURIHandler(this);
}
DownloadStream downloadStream = new DownloadStream(
new ByteArrayInputStream(responce.getBytes()),
"text/html", "loginSuccesfull");
downloadStream.setCacheTime(-1);
return downloadStream;
} else {
return null;
}
}
};
private Window window;
public LoginForm() {
iframe.setType(Embedded.TYPE_BROWSER);
iframe.setSizeFull();
setSizeFull();
setCompositionRoot(iframe);
addStyleName("v-loginform");
}
/**
* Returns byte array containing login page html. If you need to override
* the login html, use the default html as basis. Login page sets its target
* with javascript.
*
* @return byte array containing login page html
*/
protected byte[] getLoginHTML() {
String appUri = getApplication().getURL().toString()
+ getWindow().getName() + "/";
return ("\n" + ""
+ ""
+ ""
+ "" + "")
.getBytes();
}
@Override
public void attach() {
super.attach();
getApplication().addResource(loginPage);
getWindow().addParameterHandler(paramHandler);
iframe.setSource(loginPage);
}
@Override
public void detach() {
getApplication().removeResource(loginPage);
getWindow().removeParameterHandler(paramHandler);
// store window temporary to properly remove uri handler once
// response is handled. (May happen if login handler removes login
// form
window = getWindow();
if (window.getParent() != null) {
window = window.getParent();
}
super.detach();
}
/**
* This event is sent when login form is submitted.
*/
public class LoginEvent extends Event {
private Map params;
private LoginEvent(Map params) {
super(LoginForm.this);
this.params = params;
}
/**
* Access method to form values by field names.
*
* @param name
* @return value in given field
*/
public String getLoginParameter(String name) {
if (params.containsKey(name)) {
return params.get(name);
} else {
return null;
}
}
}
/**
* Login listener is a class capable to listen LoginEvents sent from
* LoginBox
*/
public interface LoginListener extends Serializable {
/**
* This method is fired on each login form post.
*
* @param event
*/
public void onLogin(LoginForm.LoginEvent event);
}
private static final Method ON_LOGIN_METHOD;
private static final String UNDEFINED_HEIGHT = "140px";
private static final String UNDEFINED_WIDTH = "200px";
static {
try {
ON_LOGIN_METHOD = LoginListener.class.getDeclaredMethod("onLogin",
new Class[] { LoginEvent.class });
} catch (final java.lang.NoSuchMethodException e) {
// This should never happen
throw new java.lang.RuntimeException(
"Internal error finding methods in LoginForm");
}
}
/**
* Adds LoginListener to handle login logic
*
* @param listener
*/
public void addListener(LoginListener listener) {
addListener(LoginEvent.class, listener, ON_LOGIN_METHOD);
}
/**
* Removes LoginListener
*
* @param listener
*/
public void removeListener(LoginListener listener) {
removeListener(LoginEvent.class, listener, ON_LOGIN_METHOD);
}
@Override
public void setWidth(float width, int unit) {
super.setWidth(width, unit);
if (iframe != null) {
if (width < 0) {
iframe.setWidth(UNDEFINED_WIDTH);
} else {
iframe.setWidth("100%");
}
}
}
@Override
public void setHeight(float height, int unit) {
super.setHeight(height, unit);
if (iframe != null) {
if (height < 0) {
iframe.setHeight(UNDEFINED_HEIGHT);
} else {
iframe.setHeight("100%");
}
}
}
}