You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

LoginForm.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. /*
  2. * Copyright 2000-2014 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.ui;
  17. import java.io.ByteArrayInputStream;
  18. import java.io.InputStream;
  19. import java.io.Serializable;
  20. import java.io.UnsupportedEncodingException;
  21. import java.lang.reflect.Method;
  22. import java.util.HashMap;
  23. import java.util.Map;
  24. import com.vaadin.server.StreamResource;
  25. import com.vaadin.shared.ui.loginform.LoginFormConstants;
  26. import com.vaadin.shared.ui.loginform.LoginFormRpc;
  27. import com.vaadin.shared.ui.loginform.LoginFormState;
  28. /**
  29. * Login form with auto-completion and auto-fill for all major browsers. You can
  30. * derive from this class and implement the
  31. * {@link #createContent(com.vaadin.ui.TextField, com.vaadin.ui.PasswordField, com.vaadin.ui.Button)}
  32. * method to build the layout using the text fields and login button that are
  33. * passed to that method. The supplied components are specially treated so that
  34. * they work with password managers.
  35. * <p>
  36. * If you need to change the URL as part of the login procedure, call
  37. * {@link #setLoginMode(LoginMode)} with the argument {@link LoginMode#DEFERRED}
  38. * in your implementation of
  39. * {@link #createContent(com.vaadin.ui.TextField, com.vaadin.ui.PasswordField, com.vaadin.ui.Button)
  40. * createContent}.
  41. * <p>
  42. * To customize the fields or to replace them with your own implementations, you
  43. * can override {@link #createUsernameField()}, {@link #createPasswordField()}
  44. * and {@link #createLoginButton()}. These methods are called automatically and
  45. * cannot be called by your code. Captions can be reset by overriding
  46. * {@link #getUsernameFieldCaption()}, {@link #getPasswordFieldCaption()} and
  47. * {@link #getLoginButtonCaption()}.
  48. * <p>
  49. * Note that the API of LoginForm changed significantly in Vaadin 7.7.
  50. *
  51. * @since 5.3
  52. */
  53. public class LoginForm extends AbstractSingleComponentContainer {
  54. /**
  55. * This event is sent when login form is submitted.
  56. */
  57. public static class LoginEvent extends Event {
  58. private Map<String, String> params;
  59. private LoginEvent(Component source, Map<String, String> params) {
  60. super(source);
  61. this.params = params;
  62. }
  63. /**
  64. * Access method to form values by field names.
  65. *
  66. * @param name
  67. * @return value in given field
  68. */
  69. public String getLoginParameter(String name) {
  70. if (params.containsKey(name)) {
  71. return params.get(name);
  72. } else {
  73. return null;
  74. }
  75. }
  76. }
  77. /**
  78. * Login listener is a class capable to listen LoginEvents sent from
  79. * LoginBox
  80. */
  81. public interface LoginListener extends Serializable {
  82. /**
  83. * This method is fired on each login form post.
  84. *
  85. * @param event
  86. * Login event
  87. */
  88. public void onLogin(LoginEvent event);
  89. }
  90. /**
  91. * Internal stream source for the login URL - always returns "Success" and
  92. * ignores the values received.
  93. */
  94. private static class LoginStreamSource implements StreamResource.StreamSource {
  95. @Override
  96. public InputStream getStream() {
  97. try {
  98. return new ByteArrayInputStream("<html>Success</html>".toString().getBytes(
  99. "UTF-8"));
  100. } catch (UnsupportedEncodingException e) {
  101. return null;
  102. }
  103. }
  104. }
  105. static {
  106. try {
  107. ON_LOGIN_METHOD = LoginListener.class.getDeclaredMethod("onLogin",
  108. new Class[] { LoginEvent.class });
  109. } catch (final java.lang.NoSuchMethodException e) {
  110. // This should never happen
  111. throw new java.lang.RuntimeException(
  112. "Internal error finding methods in LoginForm");
  113. }
  114. }
  115. private static final Method ON_LOGIN_METHOD;
  116. private boolean initialized;
  117. private String usernameCaption = "Username";
  118. private String passwordCaption = "Password";
  119. private String loginButtonCaption = "Login";
  120. /**
  121. * Customize the user name field. Only for overriding, do not call.
  122. *
  123. * @return the user name field
  124. * @since 7.7
  125. */
  126. protected TextField createUsernameField() {
  127. checkInitialized();
  128. TextField field = new TextField(getUsernameCaption());
  129. field.focus();
  130. return field;
  131. }
  132. /**
  133. * Returns the caption set with {@link #setUsernameCaption(String)}. Note
  134. * that this method might not match what is shown to the user if
  135. * {@link #createUsernameField()} has been overridden.
  136. *
  137. * @return user name field caption
  138. */
  139. public String getUsernameCaption() {
  140. return usernameCaption;
  141. }
  142. /**
  143. * Set the caption of the user name field. Note that the caption can only be
  144. * set with this method before the login form has been initialized
  145. * (attached).
  146. * <p>
  147. * As an alternative to calling this method, the method
  148. * {@link #createUsernameField()} can be overridden.
  149. *
  150. * @param cap
  151. * new caption
  152. */
  153. public void setUsernameCaption(String cap) {
  154. usernameCaption = cap;
  155. }
  156. /**
  157. * Customize the password field. Only for overriding, do not call.
  158. *
  159. * @return the password field
  160. * @since 7.7
  161. */
  162. protected PasswordField createPasswordField() {
  163. checkInitialized();
  164. return new PasswordField(getPasswordCaption());
  165. }
  166. /**
  167. * Returns the caption set with {@link #setPasswordCaption(String)}. Note
  168. * that this method might not match what is shown to the user if
  169. * {@link #createPasswordField()} has been overridden.
  170. *
  171. * @return password field caption
  172. */
  173. public String getPasswordCaption() {
  174. return passwordCaption;
  175. }
  176. /**
  177. * Set the caption of the password field. Note that the caption can only be
  178. * set with this method before the login form has been initialized
  179. * (attached).
  180. * <p>
  181. * As an alternative to calling this method, the method
  182. * {@link #createPasswordField()} can be overridden.
  183. *
  184. * @param cap new caption
  185. */
  186. public void setPasswordCaption(String cap) {
  187. passwordCaption = cap;
  188. ;
  189. }
  190. /**
  191. * Customize the login button. Only for overriding, do not call.
  192. *
  193. * @return the login button
  194. * @since 7.7
  195. */
  196. protected Button createLoginButton() {
  197. checkInitialized();
  198. return new Button(getLoginButtonCaption());
  199. }
  200. /**
  201. * Returns the caption set with {@link #setLoginButtonCaption(String)}. Note
  202. * that this method might not match what is shown to the user if
  203. * {@link #createLoginButton()} has been overridden.
  204. *
  205. * @return login button caption
  206. */
  207. public String getLoginButtonCaption() {
  208. return loginButtonCaption;
  209. }
  210. /**
  211. * Set the caption of the login button. Note that the caption can only be
  212. * set with this method before the login form has been initialized
  213. * (attached).
  214. * <p>
  215. * As an alternative to calling this method, the method
  216. * {@link #createLoginButton()} can be overridden.
  217. *
  218. * @param cap new caption
  219. */
  220. public void setLoginButtonCaption(String cap) {
  221. loginButtonCaption = cap;
  222. }
  223. @Override
  224. protected LoginFormState getState() {
  225. return (LoginFormState) super.getState();
  226. }
  227. @Override
  228. public void attach() {
  229. super.attach();
  230. init();
  231. }
  232. private void checkInitialized() {
  233. if (initialized) {
  234. throw new IllegalStateException(
  235. "Already initialized. The create methods may not be called explicitly.");
  236. }
  237. }
  238. /**
  239. * Create the content for the login form with the supplied user name field,
  240. * password field and the login button. You cannot use any other text fields
  241. * or buttons for this purpose. To replace these components with your own
  242. * implementations, override {@link #createUsernameField()},
  243. * {@link #createPasswordField()} and {@link #createLoginButton()}. If you
  244. * only want to change the default captions, override
  245. * {@link #getUsernameFieldCaption()}, {@link #getPasswordFieldCaption()}
  246. * and {@link #getLoginButtonCaption()}. You do not have to use the login
  247. * button in your layout.
  248. *
  249. * @param userNameField
  250. * the user name text field
  251. * @param passwordField
  252. * the password field
  253. * @param loginButton
  254. * the login button
  255. * @return content component
  256. * @since 7.7
  257. */
  258. protected Component createContent(TextField userNameField,
  259. PasswordField passwordField, Button loginButton) {
  260. VerticalLayout layout = new VerticalLayout();
  261. layout.setSpacing(true);
  262. layout.setMargin(true);
  263. layout.addComponent(userNameField);
  264. layout.addComponent(passwordField);
  265. layout.addComponent(loginButton);
  266. return layout;
  267. }
  268. private void init() {
  269. if (initialized) {
  270. return;
  271. }
  272. LoginFormState state = getState();
  273. state.userNameFieldConnector = createUsernameField();
  274. state.passwordFieldConnector = createPasswordField();
  275. state.loginButtonConnector = createLoginButton();
  276. StreamResource resource = new StreamResource(new LoginStreamSource(),
  277. LoginFormConstants.LOGIN_RESOURCE_NAME);
  278. resource.setMIMEType("text/html; charset=utf-8");
  279. resource.setCacheTime(-1);
  280. setResource(LoginFormConstants.LOGIN_RESOURCE_NAME, resource);
  281. registerRpc(new LoginFormRpc() {
  282. @Override
  283. public void submitCompleted() {
  284. login();
  285. }
  286. });
  287. initialized = true;
  288. setContent(createContent(getUsernameField(), getPasswordField(),
  289. getLoginButton()));
  290. }
  291. private TextField getUsernameField() {
  292. return (TextField) getState().userNameFieldConnector;
  293. }
  294. private PasswordField getPasswordField() {
  295. return (PasswordField) getState().passwordFieldConnector;
  296. }
  297. private Button getLoginButton() {
  298. return (Button) getState().loginButtonConnector;
  299. }
  300. /*
  301. * (non-Javadoc)
  302. *
  303. * Handle the login. In deferred mode, this method is called after the dummy
  304. * POST request that triggers the password manager has been completed. In
  305. * direct mode (the default setting), it is called directly when the user
  306. * hits the enter key or clicks on the login button. In the latter case, you
  307. * cannot change the URL in the method or the password manager will not be
  308. * triggered.
  309. */
  310. private void login() {
  311. HashMap<String, String> params = new HashMap<String, String>();
  312. params.put("username", getUsernameField().getValue());
  313. params.put("password", getPasswordField().getValue());
  314. LoginEvent event = new LoginEvent(LoginForm.this, params);
  315. fireEvent(event);
  316. }
  317. /**
  318. * Adds LoginListener to handle login logic
  319. *
  320. * @param listener
  321. */
  322. public void addLoginListener(LoginListener listener) {
  323. addListener(LoginEvent.class, listener, ON_LOGIN_METHOD);
  324. }
  325. /**
  326. * @deprecated As of 7.0, replaced by
  327. * {@link #addLoginListener(LoginListener)}
  328. **/
  329. @Deprecated
  330. public void addListener(LoginListener listener) {
  331. addLoginListener(listener);
  332. }
  333. /**
  334. * Removes LoginListener
  335. *
  336. * @param listener
  337. */
  338. public void removeLoginListener(LoginListener listener) {
  339. removeListener(LoginEvent.class, listener, ON_LOGIN_METHOD);
  340. }
  341. /**
  342. * @deprecated As of 7.0, replaced by
  343. * {@link #removeLoginListener(LoginListener)}
  344. **/
  345. @Deprecated
  346. public void removeListener(LoginListener listener) {
  347. removeLoginListener(listener);
  348. }
  349. }