Java tutorial
/* * * PROJECT * Name * APS User Admin Web * * Code Version * 0.10.0 * * Description * This is an administration web for aps-simple-user-service that allows editing of roles and users. * * COPYRIGHTS * Copyright (C) 2012 by Natusoft AB All rights reserved. * * LICENSE * Apache 2.0 (Open Source) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * AUTHORS * Tommy Svensson (tommy@natusoft.se) * Changes: * 2012-08-26: Created! * */ package se.natusoft.osgi.aps.apsuseradminweb.vaadin.components.editors; import com.vaadin.data.Item; import com.vaadin.data.util.IndexedContainer; import com.vaadin.event.DataBoundTransferable; import com.vaadin.event.dd.DragAndDropEvent; import com.vaadin.event.dd.DropHandler; import com.vaadin.event.dd.acceptcriteria.AcceptCriterion; import com.vaadin.event.dd.acceptcriteria.ServerSideCriterion; import com.vaadin.ui.*; import se.natusoft.osgi.aps.api.auth.user.APSSimpleUserServiceAdmin; import se.natusoft.osgi.aps.api.auth.user.model.Role; import se.natusoft.osgi.aps.api.auth.user.model.RoleAdmin; import se.natusoft.osgi.aps.apsuseradminweb.vaadin.EditorIdentifier; import se.natusoft.osgi.aps.apsuseradminweb.vaadin.components.EditorPanel; import se.natusoft.osgi.aps.apsuseradminweb.vaadin.components.HelpText; import se.natusoft.osgi.aps.apsuseradminweb.vaadin.css.CSS; import java.util.List; /** * Edits a role. This component is reusable within a session. The role to edit is passed in setRole() by * EditRoleComponentHandler. */ public class RoleEditor extends EditorPanel implements EditorIdentifier { // // Constants // /** Vaadin data container key for table column 'Role id' in 'availableRoles' and 'selectedRoles' tables. */ private static final String ROLE_ID = "Role id"; /** Vaadin data container key for table column 'Role Description' in 'availableRoles' and 'selectedRoles' tables. */ private static final String ROLE_DESC = "Role Description"; // // Private Members // /** The user service for getting and changing roles with. */ private APSSimpleUserServiceAdmin userServiceAdmin = null; /** The role being edited. */ private RoleAdmin role = null; /** The role id */ private TextField idTextField = null; /** Indicates a master role. */ private CheckBox masterRole = null; /** The description of the role. */ private TextArea descriptionTextArea = null; /** * This is a Table containing all available roles minus the roles of the current user. Roles are dragged and * dropped from this table to the 'selectedRoles' table to add a role to the user. */ private Table availableRoles = null; /** * This is a table containing the current users roles. Roles are dragged and dropped from this table to the * 'availableRoles' table to remove a role from the user. */ private Table selectedRoles = null; // // Constructors // /** * Creates a new RoleEditor instance. * * @param userServiceAdmin The user admin service for editing the role. */ public RoleEditor(APSSimpleUserServiceAdmin userServiceAdmin) { this.userServiceAdmin = userServiceAdmin; this.setStyleName(CSS.APS_EDITING_TEXT); VerticalLayout verticalLayout = new VerticalLayout(); verticalLayout.setMargin(true); verticalLayout.setSpacing(true); verticalLayout.setStyleName(CSS.APS_EDITING_TEXT + " " + CSS.APS_CONTENT_PANEL); // Role id, master and description. { HorizontalLayout horizLayout = new HorizontalLayout(); horizLayout.setSpacing(true); this.idTextField = new TextField("Role id"); this.idTextField.setColumns(30); this.idTextField.setImmediate(false); this.idTextField.setEnabled(true); horizLayout.addComponent(this.idTextField); this.masterRole = new CheckBox("Master Role"); this.masterRole.setImmediate(false); this.masterRole.setEnabled(true); horizLayout.addComponent(this.masterRole); verticalLayout.addComponent(horizLayout); this.descriptionTextArea = new TextArea("Description of role"); this.descriptionTextArea.setRows(3); this.descriptionTextArea.setColumns(60); this.descriptionTextArea.setImmediate(false); this.descriptionTextArea.setEnabled(true); verticalLayout.addComponent(this.descriptionTextArea); } // Roles { HorizontalLayout rolesLayout = new HorizontalLayout(); rolesLayout.setSizeFull(); // Available this.availableRoles = new Table("Available roles"); this.availableRoles.setImmediate(true); this.availableRoles.setPageLength(10); this.availableRoles.setSortAscending(true); this.availableRoles.setSizeFull(); this.availableRoles.setDragMode(Table.TableDragMode.ROW); this.availableRoles.setDropHandler(new DropHandler() { @Override public void drop(DragAndDropEvent event) { DataBoundTransferable t = (DataBoundTransferable) event.getTransferable(); Object itemId = t.getItemId(); removeSubRole(itemId); } @Override public AcceptCriterion getAcceptCriterion() { return new RoleAcceptCriterion(RoleEditor.this.availableRoles); } }); VerticalLayout availableRolesFrame = new VerticalLayout(); availableRolesFrame.setMargin(false, true, false, false); availableRolesFrame.addComponent(this.availableRoles); rolesLayout.addComponent(availableRolesFrame); // Selected this.selectedRoles = new Table("Selected sub roles of the role"); this.selectedRoles.setImmediate(true); this.selectedRoles.setPageLength(10); this.selectedRoles.setSortAscending(true); this.selectedRoles.setSizeFull(); this.selectedRoles.setDragMode(Table.TableDragMode.ROW); this.selectedRoles.setDropHandler(new DropHandler() { @Override public void drop(DragAndDropEvent event) { DataBoundTransferable t = (DataBoundTransferable) event.getTransferable(); Object itemId = t.getItemId(); addSubRole(itemId); } @Override public AcceptCriterion getAcceptCriterion() { return new RoleAcceptCriterion(RoleEditor.this.selectedRoles); } }); VerticalLayout selectedRolesFrame = new VerticalLayout(); selectedRolesFrame.setMargin(false, false, false, true); selectedRolesFrame.addComponent(this.selectedRoles); rolesLayout.addComponent(selectedRolesFrame); rolesLayout.setExpandRatio(availableRolesFrame, 0.5f); rolesLayout.setExpandRatio(selectedRolesFrame, 0.5f); verticalLayout.addComponent(rolesLayout); /* Help text for the role tables. */ HelpText roleHelptext = new HelpText( "Drag and drop roles back and forth to set or remove a role. Also note that it is fully possible to " + "create circular role dependencies. Don't!"); verticalLayout.addComponent(roleHelptext); } // Save / Cancel { HorizontalLayout horizontalLayout = new HorizontalLayout(); verticalLayout.addComponent(horizontalLayout); horizontalLayout.setSpacing(true); Button saveButton = new Button("Save"); saveButton.addListener(new Button.ClickListener() { /** Click handling. */ @Override public void buttonClick(Button.ClickEvent event) { save(); } }); horizontalLayout.addComponent(saveButton); Button cancelButton = new Button("Cancel"); cancelButton.addListener(new Button.ClickListener() { /** Click handling. */ @Override public void buttonClick(Button.ClickEvent event) { cancel(); } }); horizontalLayout.addComponent(cancelButton); } setContent(verticalLayout); } // // Methods // /** * Saves the role changes. */ private void save() { try { String successMessage1; String successMessage2; if (this.role == null) { this.role = this.userServiceAdmin.createRole(this.idTextField.getValue().toString(), this.descriptionTextArea.getValue().toString()); this.role.setMasterRole((Boolean) this.masterRole.getValue()); successMessage1 = "New role created."; successMessage2 = "Role '" + this.role.getId() + "' was created!"; } else { this.role.setDescription(this.descriptionTextArea.getValue().toString()); successMessage1 = "Role updated."; successMessage2 = "Role '" + role.getId() + "' was updated!"; } // Roles IndexedContainer rolesContainer = (IndexedContainer) this.selectedRoles.getContainerDataSource(); for (Object itemId : rolesContainer.getItemIds()) { Item item = rolesContainer.getItem(itemId); String roleId = item.getItemProperty(ROLE_ID).getValue().toString(); if (!this.role.hasRole(roleId)) { Role role = this.userServiceAdmin.getRole(roleId); this.role.addRole(role); } } List<Role> roles = this.role.getRoles(); if (roles != null) { // TODO: We get a ConcurrentModificationException here when there are no selected roles! for (Role role : roles) { boolean roleStillValid = false; for (Object itemId : rolesContainer.getItemIds()) { Item item = rolesContainer.getItem(itemId); String roleId = item.getItemProperty(ROLE_ID).getValue().toString(); if (roleId.equals(role.getId())) { roleStillValid = true; break; } } if (!roleStillValid) { this.role.removeRole(role); } } } this.userServiceAdmin.updateRole(this.role); notifySuccess(successMessage1, successMessage2); refreshDependentComponents(); } catch (RuntimeException re) { notifyError("Failed saving role!", "Falied to save role:" + re.getMessage()); getLogger().error("Failed to save role!", re); throw re; } } /** * Cancels all changes. */ private void cancel() { clearCenter(); refreshDependentComponents(); notifySuccess("Role change cancelled", "Role change " + (this.role != null ? "for '" + this.role.getId() + "' " : "") + "cancelled!"); } /** * Sets the role to work on. * * @param role The role to edit. */ public void setRole(RoleAdmin role) { this.role = role; if (role != null) { setCaption("Editing role '" + role.getId() + "'"); this.idTextField.setValue(this.role.getId()); this.idTextField.setEnabled(false); this.masterRole.setValue(role.isMasterRole()); this.masterRole.setEnabled(false); this.descriptionTextArea.setValue(this.role.getDescription()); this.availableRoles.setContainerDataSource(createAvailableRolesContainer(role)); this.selectedRoles.setContainerDataSource(createSubRolesContainer(role)); } else { setCaption("Editing new role"); this.idTextField.setValue(""); this.idTextField.setEnabled(true); this.masterRole.setValue(false); this.masterRole.setEnabled(true); this.descriptionTextArea.setValue(""); this.availableRoles.setContainerDataSource(createAvailableRolesContainer(null)); this.selectedRoles.setContainerDataSource(createSubRolesContainer(null)); } } /** * Implementation of EditorIdentifier. * * @return The id for this editor. */ @Override public String getEditorId() { return this.role != null ? this.role.getId() : ""; } /** * Adds a role to the user. This is called by the DropHandler for 'selectedRoles' table when a new role * is dropped on it. * * @param itemId This identifies the item of the 'availableRoles' table that was dropped. */ private void addSubRole(Object itemId) { if (itemId != null) { Item item = this.availableRoles.getItem(itemId); String roleId = (String) item.getItemProperty(ROLE_ID).getValue(); String roleDesc = (String) item.getItemProperty(ROLE_DESC).getValue(); this.availableRoles.removeItem(itemId); item = this.selectedRoles.addItem(roleId); item.getItemProperty(ROLE_ID).setValue(roleId); item.getItemProperty(ROLE_DESC).setValue(roleDesc); IndexedContainer selectedRolesContainer = (IndexedContainer) this.selectedRoles .getContainerDataSource(); selectedRolesContainer.sort(new Object[] { ROLE_ID }, new boolean[] { true }); } } /** * Removes a role from the user. This is called by the DropHandler for 'availableRoles' table when a non contained * role is dropped on it from the 'selectedRoles' table. Please note that a role only exists in one or the other * of the two role tables. * * @param itemId This identifies the item of the 'selectedRoles' table that was dropped. */ private void removeSubRole(Object itemId) { if (itemId == null) { itemId = this.selectedRoles.getValue(); } if (itemId != null) { Item item = this.selectedRoles.getItem(itemId); String roleId = (String) item.getItemProperty(ROLE_ID).getValue(); String roleDesc = (String) item.getItemProperty(ROLE_DESC).getValue(); this.selectedRoles.removeItem(itemId); item = this.availableRoles.addItem(roleId); item.getItemProperty(ROLE_ID).setValue(roleId); item.getItemProperty(ROLE_DESC).setValue(roleDesc); IndexedContainer availRolesContainer = (IndexedContainer) this.availableRoles.getContainerDataSource(); availRolesContainer.sort(new Object[] { ROLE_ID }, new boolean[] { true }); } } /** * Factory method to create a Vaadin IndexedContainer containing all available roles minus the specified * role:s all sub roles. This is used by the 'availableRoles' Table. * * @param role The user whose roles should be subtracted for all roles. * * @return The created container. */ private IndexedContainer createAvailableRolesContainer(RoleAdmin role) { IndexedContainer availRolesContainer = new IndexedContainer(); availRolesContainer.addContainerProperty(ROLE_ID, String.class, ""); availRolesContainer.addContainerProperty(ROLE_DESC, String.class, ""); for (Role subrole : this.userServiceAdmin.getRoles()) { if (!subrole.isMasterRole()) { // We can only add sub roles to other roles. Item item = availRolesContainer.addItem(subrole.getId()); item.getItemProperty(ROLE_ID).setValue(subrole.getId()); item.getItemProperty(ROLE_DESC).setValue(subrole.getDescription()); } } // Remove the sub roles already set on the role. if (role != null) { for (Role subrole : role.getRoles()) { availRolesContainer.removeItem(subrole.getId()); } } availRolesContainer.sort(new Object[] { ROLE_ID }, new boolean[] { true }); return availRolesContainer; } /** * Factory method to create a Vaadin IndexedContainer containing the specified roles all sub roles. * This is used by the 'selectedRoles' Table. * * @param role The user to get the roles from. * * @return The created container. */ private IndexedContainer createSubRolesContainer(RoleAdmin role) { IndexedContainer userRolesContainer = new IndexedContainer(); userRolesContainer.addContainerProperty(ROLE_ID, String.class, ""); userRolesContainer.addContainerProperty(ROLE_DESC, String.class, ""); if (role != null) { for (Role subrole : role.getRoles()) { Item item = userRolesContainer.addItem(subrole.getId()); item.getItemProperty(ROLE_ID).setValue(subrole.getId()); item.getItemProperty(ROLE_DESC).setValue(subrole.getDescription()); } } userRolesContainer.sort(new Object[] { ROLE_ID }, new boolean[] { true }); return userRolesContainer; } /** * This is the "accept criterion" used by both DropHandler for 'availableRoles' and 'selectedRoles'. * It accepts any drop of a value that does not already exists in the target. This is kind of redundant * since the contents of the two above mentioned Tables are (should be) unique in conjunction. A role * can only be in one or the other never both at the same time. * <p/> * Vaadin however refused to do any dropping at all without this (I tried to keep it on the client * side until dropped, but failed). I'm hoping to resolve this and remove this inner class. */ private static class RoleAcceptCriterion extends ServerSideCriterion { // // Private Members // /** The Table this criterion is for. */ private Table sourceTable = null; // // Constructors // /** * Creates a new RoleAcceptCriterion. * * @param sourceTable The Table using this criterion. */ public RoleAcceptCriterion(Table sourceTable) { this.sourceTable = sourceTable; } // // Methods // /** * Validates the data in event to be appropriate for the * {@link com.vaadin.event.dd.DropHandler#drop(com.vaadin.event.dd.DragAndDropEvent)} method. * <p/> * Note that even if your criterion is validated on client side, you should * always validate the data on server side too. * * @param dragEvent * @return */ @Override public boolean accept(DragAndDropEvent dragEvent) { DataBoundTransferable t = (DataBoundTransferable) dragEvent.getTransferable(); Object itemId = t.getItemId(); IndexedContainer container = (IndexedContainer) this.sourceTable.getContainerDataSource(); return !container.containsId(itemId); } } }