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

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