Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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. */ package org.apache.syncope.client.console.wizards.resources; import de.agilecoders.wicket.core.markup.html.bootstrap.components.PopoverBehavior; import de.agilecoders.wicket.core.markup.html.bootstrap.components.PopoverConfig; import de.agilecoders.wicket.core.markup.html.bootstrap.components.TooltipConfig; import java.io.Serializable; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang3.ClassUtils; import org.apache.syncope.client.console.commons.ConnIdSpecialAttributeName; import org.apache.syncope.client.console.commons.Constants; import org.apache.syncope.client.console.rest.AnyTypeClassRestClient; import org.apache.syncope.client.console.rest.AnyTypeRestClient; import org.apache.syncope.client.console.rest.ConnectorRestClient; import org.apache.syncope.client.console.wicket.ajax.form.IndicatorAjaxFormComponentUpdatingBehavior; import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink; import org.apache.syncope.client.console.wicket.markup.html.form.ActionLinksPanel; import org.apache.syncope.client.console.wicket.markup.html.form.AjaxCheckBoxPanel; import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel; import org.apache.syncope.client.console.wicket.markup.html.form.MappingPurposePanel; import org.apache.syncope.client.console.widgets.JEXLTransformerWidget; import org.apache.syncope.client.console.widgets.MappingItemTransformerWidget; import org.apache.syncope.common.lib.to.AnyObjectTO; import org.apache.syncope.common.lib.to.AnyTypeClassTO; import org.apache.syncope.common.lib.to.AnyTypeTO; import org.apache.syncope.common.lib.to.GroupTO; import org.apache.syncope.common.lib.to.MappingItemTO; import org.apache.syncope.common.lib.to.MappingTO; import org.apache.syncope.common.lib.to.ProvisionTO; import org.apache.syncope.common.lib.to.ResourceTO; import org.apache.syncope.common.lib.to.UserTO; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.MappingPurpose; import org.apache.syncope.common.lib.types.StandardEntitlement; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.markup.html.form.AjaxButton; import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxButton; import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.list.ListItem; import org.apache.wicket.markup.html.list.ListView; import org.apache.wicket.markup.html.panel.Panel; import org.apache.wicket.model.LoadableDetachableModel; import org.apache.wicket.model.Model; import org.apache.wicket.model.PropertyModel; import org.apache.wicket.model.ResourceModel; /** * Resource mapping panel. */ public class ResourceMappingPanel extends Panel { private static final long serialVersionUID = -7982691107029848579L; private static final Set<String> USER_FIELD_NAMES = new HashSet<>(); private static final Set<String> GROUP_FIELD_NAMES = new HashSet<>(); private static final Set<String> ANY_OBJECT_FIELD_NAMES = new HashSet<>(); static { initFieldNames(UserTO.class, USER_FIELD_NAMES); initFieldNames(GroupTO.class, GROUP_FIELD_NAMES); initFieldNames(AnyObjectTO.class, ANY_OBJECT_FIELD_NAMES); } private static void initFieldNames(final Class<?> entityClass, final Set<String> keys) { List<Class<?>> classes = ClassUtils.getAllSuperclasses(entityClass); classes.add(entityClass); for (Class<?> clazz : classes) { for (Field field : clazz.getDeclaredFields()) { if (!Modifier.isStatic(field.getModifiers()) && !Collection.class.isAssignableFrom(field.getType()) && !Map.class.isAssignableFrom(field.getType())) { keys.add(field.getName()); } } } } /** * Any type rest client. */ private final AnyTypeRestClient anyTypeRestClient = new AnyTypeRestClient(); /** * Any type class rest client. */ private final AnyTypeClassRestClient anyTypeClassRestClient = new AnyTypeClassRestClient(); private final Label passwordLabel; /** * Add mapping button. */ private final AjaxButton addMappingBtn; /** * All mappings. */ private final ListView<MappingItemTO> mappings; /** * External resource provisioning configuration instance to be updated. */ private final ProvisionTO provisionTO; /** * Mapping container. */ private final WebMarkupContainer mappingContainer; /** * Attribute Mapping Panel. * * @param id panel id * @param resourceTO external resource to be updated * @param provisionTO external resource provisioning configuration instance * @param mapItemTransformers mapping item transformers toggle panel * @param jexlTransformers JEXL transformers toggle panel */ public ResourceMappingPanel(final String id, final ResourceTO resourceTO, final ProvisionTO provisionTO, final MappingItemTransformersTogglePanel mapItemTransformers, final JEXLTransformersTogglePanel jexlTransformers) { super(id); setOutputMarkupId(true); this.provisionTO = provisionTO; if (provisionTO.getMapping() == null) { provisionTO.setMapping(new MappingTO()); } final LoadableDetachableModel<List<String>> extAttrNames = new LoadableDetachableModel<List<String>>() { private static final long serialVersionUID = 5275935387613157437L; @Override protected List<String> load() { return new ConnectorRestClient().getExtAttrNames(provisionTO.getObjectClass(), resourceTO.getConnector(), resourceTO.getConfOverride()); } }; this.mappingContainer = new WebMarkupContainer("mappingContainer"); this.mappingContainer.setOutputMarkupId(true); add(this.mappingContainer); mappingContainer.add(new Label("intAttrNameInfo", Model.of()).add(new PopoverBehavior(Model.<String>of(), Model.of(getString("intAttrNameInfo.help") + "<div style=\"font-size: 10px;\">" + "<code>groups[groupName].attribute</code>\n" + "<code>anyObjects[anyObjectName].attribute</code>\n" + "<code>memberships[groupName].attribute</code>\n" + "</div>"), new PopoverConfig().withHtml(true).withPlacement(TooltipConfig.Placement.bottom)) { private static final long serialVersionUID = -7867802555691605021L; @Override protected String createRelAttribute() { return "intAttrNameInfo"; } })); mappingContainer.add(Constants.getJEXLPopover(this, TooltipConfig.Placement.bottom)); passwordLabel = new Label("passwordLabel", new ResourceModel("password")); mappingContainer.add(passwordLabel); Collections.sort(provisionTO.getMapping().getItems(), new Comparator<MappingItemTO>() { @Override public int compare(final MappingItemTO left, final MappingItemTO right) { int compared; if (left == null && right == null) { compared = 0; } else if (left == null) { compared = 1; } else if (right == null) { compared = -1; } else if (left.isConnObjectKey()) { compared = -1; } else if (right.isConnObjectKey()) { compared = 1; } else if (left.isPassword()) { compared = -1; } else if (right.isPassword()) { compared = 1; } else if (left.getPurpose() == MappingPurpose.BOTH && right.getPurpose() != MappingPurpose.BOTH) { compared = -1; } else if (left.getPurpose() != MappingPurpose.BOTH && right.getPurpose() == MappingPurpose.BOTH) { compared = 1; } else if (left.getPurpose() == MappingPurpose.PROPAGATION && (right.getPurpose() == MappingPurpose.PULL || right.getPurpose() == MappingPurpose.NONE)) { compared = -1; } else if (left.getPurpose() == MappingPurpose.PULL && right.getPurpose() == MappingPurpose.PROPAGATION) { compared = 1; } else if (left.getPurpose() == MappingPurpose.PULL && right.getPurpose() == MappingPurpose.NONE) { compared = -1; } else if (left.getPurpose() == MappingPurpose.NONE && right.getPurpose() != MappingPurpose.NONE) { compared = 1; } else { compared = left.getIntAttrName().compareTo(right.getIntAttrName()); } return compared; } }); mappings = new ListView<MappingItemTO>("mappings", provisionTO.getMapping().getItems()) { private static final long serialVersionUID = 4949588177564901031L; @Override protected void populateItem(final ListItem<MappingItemTO> item) { final MappingItemTO mapItem = item.getModelObject(); if (mapItem.getPurpose() == null) { mapItem.setPurpose(MappingPurpose.BOTH); } //-------------------------------- // Internal attribute // ------------------------------- AjaxTextFieldPanel intAttrName = new AjaxTextFieldPanel("intAttrName", getString("intAttrName"), new PropertyModel<String>(mapItem, "intAttrName"), false); intAttrName.setChoices(Collections.<String>emptyList()); intAttrName.setRequired(true).hideLabel(); item.add(intAttrName); // ------------------------------- //-------------------------------- // External attribute // ------------------------------- final AjaxTextFieldPanel extAttrName = new AjaxTextFieldPanel("extAttrName", getString("extAttrName"), new PropertyModel<String>(mapItem, "extAttrName")); extAttrName.setChoices(extAttrNames.getObject()); boolean required = !mapItem.isPassword(); extAttrName.setRequired(required).hideLabel(); extAttrName.setEnabled(required); item.add(extAttrName); // ------------------------------- //-------------------------------- // JEXL transformers // ------------------------------- item.add(new JEXLTransformerWidget("jexlTransformers", mapItem, jexlTransformers) .setRenderBodyOnly(true)); // ------------------------------- //-------------------------------- // Mapping item transformers // ------------------------------- item.add(new MappingItemTransformerWidget("mappingItemTransformers", mapItem, mapItemTransformers) .setRenderBodyOnly(true)); // ------------------------------- //-------------------------------- // Mandatory // ------------------------------- final AjaxTextFieldPanel mandatory = new AjaxTextFieldPanel("mandatoryCondition", new ResourceModel("mandatoryCondition", "mandatoryCondition").getObject(), new PropertyModel<String>(mapItem, "mandatoryCondition")); mandatory.hideLabel(); mandatory.setChoices(Arrays.asList(new String[] { "true", "false" })); mandatory.setEnabled(!mapItem.isConnObjectKey()); item.add(mandatory); // ------------------------------- //-------------------------------- // Connector object key // ------------------------------- final AjaxCheckBoxPanel connObjectKey = new AjaxCheckBoxPanel("connObjectKey", new ResourceModel("connObjectKey", "connObjectKey").getObject(), new PropertyModel<Boolean>(mapItem, "connObjectKey"), false); connObjectKey.hideLabel(); item.add(connObjectKey); // ------------------------------- //-------------------------------- // Password // ------------------------------- final AjaxCheckBoxPanel password = new AjaxCheckBoxPanel("password", new ResourceModel("password", "password").getObject(), new PropertyModel<Boolean>(mapItem, "password"), false); item.add(password.hideLabel()); // ------------------------------- //-------------------------------- // Purpose // ------------------------------- WebMarkupContainer purpose = new WebMarkupContainer("purpose"); purpose.setOutputMarkupId(Boolean.TRUE); final MappingPurposePanel panel = new MappingPurposePanel("purposeActions", new PropertyModel<MappingPurpose>(mapItem, "purpose"), purpose); purpose.add(panel.setRenderBodyOnly(true)); item.add(purpose); // ------------------------------- //-------------------------------- // Remove // ------------------------------- final ActionLinksPanel.Builder<Serializable> actions = ActionLinksPanel.builder(); actions.add(new ActionLink<Serializable>() { private static final long serialVersionUID = -3722207913631435501L; @Override public void onClick(final AjaxRequestTarget target, final Serializable ignore) { int index = -1; for (int i = 0; i < provisionTO.getMapping().getItems().size() && index == -1; i++) { if (mapItem.equals(provisionTO.getMapping().getItems().get(i))) { index = i; } } if (index != -1) { provisionTO.getMapping().getItems().remove(index); item.getParent().removeAll(); target.add(ResourceMappingPanel.this); } } }, ActionLink.ActionType.DELETE, StandardEntitlement.RESOURCE_UPDATE); item.add(actions.build("toRemove")); // ------------------------------- intAttrName.getField().add(new IndicatorAjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) { private static final long serialVersionUID = -1107858522700306810L; @Override protected void onUpdate(final AjaxRequestTarget target) { } }); connObjectKey.getField().add(new IndicatorAjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) { private static final long serialVersionUID = -1107858522700306810L; @Override protected void onUpdate(final AjaxRequestTarget target) { if (connObjectKey.getModelObject()) { mapItem.setMandatoryCondition("true"); mandatory.setModelObject("true"); mandatory.setEnabled(false); } else { mapItem.setMandatoryCondition("false"); mandatory.setModelObject("false"); mandatory.setEnabled(true); } target.add(mandatory); } }); password.getField().add(new IndicatorAjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) { private static final long serialVersionUID = -1107858522700306810L; @Override protected void onUpdate(final AjaxRequestTarget target) { extAttrName.setEnabled(!mapItem.isConnObjectKey() && !password.getModelObject()); extAttrName.setModelObject(password.getModelObject() ? ConnIdSpecialAttributeName.PASSWORD : extAttrName.getModelObject()); extAttrName.setRequired(!password.getModelObject()); target.add(extAttrName); setConnObjectKey(connObjectKey, password); target.add(connObjectKey); } }); setConnObjectKey(connObjectKey, password); setAttrNames(intAttrName); if (!AnyTypeKind.USER.name().equals(provisionTO.getAnyType())) { password.setVisible(false); // Changes required by clone .... extAttrName.setEnabled(!mapItem.isConnObjectKey()); if (mapItem.isPassword()) { // re-enable if and only if cloned objec mapping item was a password intAttrName.setEnabled(true); } mapItem.setPassword(false); } } }; mappings.setReuseItems(true); mappingContainer.add(mappings); addMappingBtn = new IndicatingAjaxButton("addMappingBtn", new ResourceModel("add")) { private static final long serialVersionUID = -4804368561204623354L; @Override protected void onSubmit(final AjaxRequestTarget target, final Form<?> form) { provisionTO.getMapping().getItems().add(new MappingItemTO()); target.add(ResourceMappingPanel.this); } }; addMappingBtn.setDefaultFormProcessing(false); addMappingBtn.setEnabled(resourceTO.getConnector() != null); mappingContainer.add(addMappingBtn); } @Override protected void onBeforeRender() { super.onBeforeRender(); passwordLabel.setVisible(AnyTypeKind.USER.name().equals(this.provisionTO.getAnyType())); } /** * Set attribute names for a drop down choice list. * * @param type attribute type. * @param toBeUpdated drop down choice to be updated. */ private void setAttrNames(final AjaxTextFieldPanel toBeUpdated) { toBeUpdated.setRequired(true); toBeUpdated.setEnabled(true); AnyTypeTO anyTypeTO = anyTypeRestClient.read(provisionTO.getAnyType()); List<AnyTypeClassTO> anyTypeClassTOs = new ArrayList<>(); anyTypeClassTOs.addAll(anyTypeClassRestClient.list(anyTypeTO.getClasses())); for (String auxClass : provisionTO.getAuxClasses()) { anyTypeClassTOs.add(anyTypeClassRestClient.read(auxClass)); } List<String> choices = new ArrayList<>(); switch (provisionTO.getAnyType()) { case "USER": choices.addAll(USER_FIELD_NAMES); break; case "GROUP": choices.addAll(GROUP_FIELD_NAMES); break; default: choices.addAll(ANY_OBJECT_FIELD_NAMES); } for (AnyTypeClassTO anyTypeClassTO : anyTypeClassTOs) { choices.addAll(anyTypeClassTO.getPlainSchemas()); choices.addAll(anyTypeClassTO.getDerSchemas()); choices.addAll(anyTypeClassTO.getVirSchemas()); } Collections.sort(choices); toBeUpdated.setChoices(choices); } /** * Enable/Disable connObjectKey checkbox. * * @param connObjectKey connObjectKey checkbox. * @param password password checkbox. */ private void setConnObjectKey(final AjaxCheckBoxPanel connObjectKey, final AjaxCheckBoxPanel password) { if (password.getModelObject()) { connObjectKey.setReadOnly(true); connObjectKey.setModelObject(false); } else { connObjectKey.setReadOnly(false); } } }