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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  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. /**
  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
  95. implements StreamResource.StreamSource {
  96. @Override
  97. public InputStream getStream() {
  98. try {
  99. return new ByteArrayInputStream(
  100. "<html>Success</html>".toString().getBytes("UTF-8"));
  101. } catch (UnsupportedEncodingException e) {
  102. return null;
  103. }
  104. }
  105. }
  106. static {
  107. try {
  108. ON_LOGIN_METHOD = LoginListener.class.getDeclaredMethod("onLogin",
  109. new Class[] { LoginEvent.class });
  110. } catch (final java.lang.NoSuchMethodException e) {
  111. // This should never happen
  112. throw new java.lang.RuntimeException(
  113. "Internal error finding methods in LoginForm");
  114. }
  115. }
  116. private static final Method ON_LOGIN_METHOD;
  117. private boolean initialized;
  118. private String usernameCaption = "Username";
  119. private String passwordCaption = "Password";
  120. private String loginButtonCaption = "Login";
  121. /**
  122. * Customize the user name field. Only for overriding, do not call.
  123. *
  124. * @return the user name field
  125. * @since 7.7
  126. */
  127. protected TextField createUsernameField() {
  128. checkInitialized();
  129. TextField field = new TextField(getUsernameCaption());
  130. field.focus();
  131. return field;
  132. }
  133. /**
  134. * Returns the caption set with {@link #setUsernameCaption(String)}. Note
  135. * that this method might not match what is shown to the user if
  136. * {@link #createUsernameField()} has been overridden.
  137. *
  138. * @return user name field caption
  139. */
  140. public String getUsernameCaption() {
  141. return usernameCaption;
  142. }
  143. /**
  144. * Set the caption of the user name field. Note that the caption can only be
  145. * set with this method before the login form has been initialized
  146. * (attached).
  147. * <p>
  148. * As an alternative to calling this method, the method
  149. * {@link #createUsernameField()} can be overridden.
  150. *
  151. * @param cap
  152. * new caption
  153. */
  154. public void setUsernameCaption(String cap) {
  155. usernameCaption = cap;
  156. }
  157. /**
  158. * Customize the password field. Only for overriding, do not call.
  159. *
  160. * @return the password field
  161. * @since 7.7
  162. */
  163. protected PasswordField createPasswordField() {
  164. checkInitialized();
  165. return new PasswordField(getPasswordCaption());
  166. }
  167. /**
  168. * Returns the caption set with {@link #setPasswordCaption(String)}. Note
  169. * that this method might not match what is shown to the user if
  170. * {@link #createPasswordField()} has been overridden.
  171. *
  172. * @return password field caption
  173. */
  174. public String getPasswordCaption() {
  175. return passwordCaption;
  176. }
  177. /**
  178. * Set the caption of the password field. Note that the caption can only be
  179. * set with this method before the login form has been initialized
  180. * (attached).
  181. * <p>
  182. * As an alternative to calling this method, the method
  183. * {@link #createPasswordField()} can be overridden.
  184. *
  185. * @param cap
  186. * new caption
  187. */
  188. public void setPasswordCaption(String cap) {
  189. passwordCaption = cap;
  190. ;
  191. }
  192. /**
  193. * Customize the login button. Only for overriding, do not call.
  194. *
  195. * @return the login button
  196. * @since 7.7
  197. */
  198. protected Button createLoginButton() {
  199. checkInitialized();
  200. return new Button(getLoginButtonCaption());
  201. }
  202. /**
  203. * Returns the caption set with {@link #setLoginButtonCaption(String)}. Note
  204. * that this method might not match what is shown to the user if
  205. * {@link #createLoginButton()} has been overridden.
  206. *
  207. * @return login button caption
  208. */
  209. public String getLoginButtonCaption() {
  210. return loginButtonCaption;
  211. }
  212. /**
  213. * Set the caption of the login button. Note that the caption can only be
  214. * set with this method before the login form has been initialized
  215. * (attached).
  216. * <p>
  217. * As an alternative to calling this method, the method
  218. * {@link #createLoginButton()} can be overridden.
  219. *
  220. * @param cap
  221. * new caption
  222. */
  223. public void setLoginButtonCaption(String cap) {
  224. loginButtonCaption = cap;
  225. }
  226. @Override
  227. protected LoginFormState getState() {
  228. return (LoginFormState) super.getState();
  229. }
  230. @Override
  231. public void attach() {
  232. super.attach();
  233. init();
  234. }
  235. private void checkInitialized() {
  236. if (initialized) {
  237. throw new IllegalStateException(
  238. "Already initialized. The create methods may not be called explicitly.");
  239. }
  240. }
  241. /**
  242. * Create the content for the login form with the supplied user name field,
  243. * password field and the login button. You cannot use any other text fields
  244. * or buttons for this purpose. To replace these components with your own
  245. * implementations, override {@link #createUsernameField()},
  246. * {@link #createPasswordField()} and {@link #createLoginButton()}. If you
  247. * only want to change the default captions, override
  248. * {@link #getUsernameFieldCaption()}, {@link #getPasswordFieldCaption()}
  249. * and {@link #getLoginButtonCaption()}. You do not have to use the login
  250. * button in your layout.
  251. *
  252. * @param userNameField
  253. * the user name text field
  254. * @param passwordField
  255. * the password field
  256. * @param loginButton
  257. * the login button
  258. * @return content component
  259. * @since 7.7
  260. */
  261. protected Component createContent(TextField userNameField,
  262. PasswordField passwordField, Button loginButton) {
  263. VerticalLayout layout = new VerticalLayout();
  264. layout.setSpacing(true);
  265. layout.setMargin(true);
  266. layout.addComponent(userNameField);
  267. layout.addComponent(passwordField);
  268. layout.addComponent(loginButton);
  269. return layout;
  270. }
  271. private void init() {
  272. if (initialized) {
  273. return;
  274. }
  275. LoginFormState state = getState();
  276. state.userNameFieldConnector = createUsernameField();
  277. state.passwordFieldConnector = createPasswordField();
  278. state.loginButtonConnector = createLoginButton();
  279. StreamResource resource = new StreamResource(new LoginStreamSource(),
  280. LoginFormConstants.LOGIN_RESOURCE_NAME);
  281. resource.setMIMEType("text/html; charset=utf-8");
  282. resource.setCacheTime(-1);
  283. setResource(LoginFormConstants.LOGIN_RESOURCE_NAME, resource);
  284. registerRpc(new LoginFormRpc() {
  285. @Override
  286. public void submitCompleted() {
  287. login();
  288. }
  289. });
  290. initialized = true;
  291. setContent(createContent(getUsernameField(), getPasswordField(),
  292. getLoginButton()));
  293. }
  294. private TextField getUsernameField() {
  295. return (TextField) getState().userNameFieldConnector;
  296. }
  297. private PasswordField getPasswordField() {
  298. return (PasswordField) getState().passwordFieldConnector;
  299. }
  300. private Button getLoginButton() {
  301. return (Button) getState().loginButtonConnector;
  302. }
  303. /*
  304. * (non-Javadoc)
  305. *
  306. * Handle the login. In deferred mode, this method is called after the dummy
  307. * POST request that triggers the password manager has been completed. In
  308. * direct mode (the default setting), it is called directly when the user
  309. * hits the enter key or clicks on the login button. In the latter case, you
  310. * cannot change the URL in the method or the password manager will not be
  311. * triggered.
  312. */
  313. private void login() {
  314. HashMap<String, String> params = new HashMap<String, String>();
  315. params.put("username", getUsernameField().getValue());
  316. params.put("password", getPasswordField().getValue());
  317. LoginEvent event = new LoginEvent(LoginForm.this, params);
  318. fireEvent(event);
  319. }
  320. /**
  321. * Adds LoginListener to handle login logic
  322. *
  323. * @param listener
  324. */
  325. public void addLoginListener(LoginListener listener) {
  326. addListener(LoginEvent.class, listener, ON_LOGIN_METHOD);
  327. }
  328. /**
  329. * @deprecated As of 7.0, replaced by
  330. * {@link #addLoginListener(LoginListener)}
  331. **/
  332. @Deprecated
  333. public void addListener(LoginListener listener) {
  334. addLoginListener(listener);
  335. }
  336. /**
  337. * Removes LoginListener
  338. *
  339. * @param listener
  340. */
  341. public void removeLoginListener(LoginListener listener) {
  342. removeListener(LoginEvent.class, listener, ON_LOGIN_METHOD);
  343. }
  344. /**
  345. * @deprecated As of 7.0, replaced by
  346. * {@link #removeLoginListener(LoginListener)}
  347. **/
  348. @Deprecated
  349. public void removeListener(LoginListener listener) {
  350. removeLoginListener(listener);
  351. }
  352. }