You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by fm...@apache.org on 2015/06/08 22:28:04 UTC
[15/17] syncope git commit: [SYNCOPE-156] working with resource and
connector topology
http://git-wip-us.apache.org/repos/asf/syncope/blob/34ec6712/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceMappingPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceMappingPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceMappingPanel.java
new file mode 100644
index 0000000..b00bc1b
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceMappingPanel.java
@@ -0,0 +1,644 @@
+/*
+ * 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.panels;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.commons.JexlHelpUtils;
+import org.apache.syncope.client.console.panels.ResourceConnConfPanel.ConnConfModEvent;
+import org.apache.syncope.client.console.rest.ConnectorRestClient;
+import org.apache.syncope.client.console.rest.SchemaRestClient;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxCheckBoxPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxDecoratedCheckbox;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxDropDownChoicePanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.FieldPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.MappingPurposePanel;
+import org.apache.syncope.common.lib.to.ConnIdObjectClassTO;
+import org.apache.syncope.common.lib.to.ConnInstanceTO;
+import org.apache.syncope.common.lib.to.MappingItemTO;
+import org.apache.syncope.common.lib.to.MappingTO;
+import org.apache.syncope.common.lib.to.ResourceTO;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.common.lib.types.ConnConfProperty;
+import org.apache.syncope.common.lib.types.IntMappingType;
+import org.apache.syncope.common.lib.types.MappingPurpose;
+import org.apache.wicket.Component;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.attributes.AjaxCallListener;
+import org.apache.wicket.ajax.attributes.AjaxRequestAttributes;
+import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
+import org.apache.wicket.ajax.markup.html.AjaxLink;
+import org.apache.wicket.ajax.markup.html.form.AjaxButton;
+import org.apache.wicket.event.IEvent;
+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.Model;
+import org.apache.wicket.model.PropertyModel;
+import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.spring.injection.annot.SpringBean;
+
+/**
+ * Resource mapping panel.
+ */
+public class ResourceMappingPanel extends Panel {
+
+ private static final long serialVersionUID = -7982691107029848579L;
+
+ /**
+ * Mapping field style sheet.
+ */
+ private static final String FIELD_STYLE = "ui-widget-content ui-corner-all short_fixedsize";
+
+ /**
+ * Mapping field style sheet.
+ */
+ private static final String DEF_FIELD_STYLE = "ui-widget-content ui-corner-all";
+
+ /**
+ * Mapping field style sheet.
+ */
+ private static final String SHORT_FIELD_STYLE = "ui-widget-content ui-corner-all veryshort_fixedsize";
+
+ /**
+ * Schema rest client.
+ */
+ @SpringBean
+ private SchemaRestClient schemaRestClient;
+
+ /**
+ * ConnInstance rest client.
+ */
+ @SpringBean
+ private ConnectorRestClient connRestClient;
+
+ /**
+ * Resource schema name.
+ */
+ private final List<String> schemaNames;
+
+ /**
+ * Add mapping button.
+ */
+ private final AjaxButton addMappingBtn;
+
+ /**
+ * All mappings.
+ */
+ private final ListView<MappingItemTO> mappings;
+
+ /**
+ * External resource to be updated.
+ */
+ private final ResourceTO resourceTO;
+
+ /**
+ * User / group.
+ */
+ private final AttributableType attrType;
+
+ /**
+ * Mapping container.
+ */
+ private final WebMarkupContainer mappingContainer;
+
+ /**
+ * AccountLink container.
+ */
+ private final WebMarkupContainer accountLinkContainer;
+
+ private final AjaxCheckBoxPanel accountLinkCheckbox;
+
+ private MappingTO getMapping() {
+ MappingTO result = null;
+
+ if (AttributableType.USER == this.attrType) {
+ if (this.resourceTO.getUmapping() == null) {
+ this.resourceTO.setUmapping(new MappingTO());
+ }
+ result = this.resourceTO.getUmapping();
+ }
+ if (AttributableType.GROUP == this.attrType) {
+ if (this.resourceTO.getGmapping() == null) {
+ this.resourceTO.setGmapping(new MappingTO());
+ }
+ result = this.resourceTO.getGmapping();
+ }
+
+ return result;
+ }
+
+ /**
+ * Attribute Mapping Panel.
+ *
+ * @param id panel id
+ * @param resourceTO external resource
+ * @param attrType USER / GROUP
+ */
+ public ResourceMappingPanel(final String id, final ResourceTO resourceTO, final AttributableType attrType) {
+ super(id);
+ setOutputMarkupId(true);
+
+ this.resourceTO = resourceTO;
+ this.attrType = attrType;
+
+ this.mappingContainer = new WebMarkupContainer("mappingContainer");
+ this.mappingContainer.setOutputMarkupId(true);
+ add(this.mappingContainer);
+
+ this.accountLinkContainer = new WebMarkupContainer("accountLinkContainer");
+ this.accountLinkContainer.setOutputMarkupId(true);
+ add(this.accountLinkContainer);
+
+ if (this.resourceTO.getConnectorId() != null && this.resourceTO.getConnectorId() > 0) {
+ schemaNames = getSchemaNames(this.resourceTO.getConnectorId(), this.resourceTO.getConnConfProperties());
+
+ setEnabled();
+ } else {
+ schemaNames = Collections.<String>emptyList();
+ }
+
+ final WebMarkupContainer jexlHelp = JexlHelpUtils.getJexlHelpWebContainer("jexlHelp");
+
+ AjaxLink<Void> questionMarkJexlHelp = JexlHelpUtils.getAjaxLink(jexlHelp, "questionMarkJexlHelp");
+ mappingContainer.add(questionMarkJexlHelp);
+ questionMarkJexlHelp.add(jexlHelp);
+
+ final Label passwordLabel = new Label("passwordLabel", new ResourceModel("password"));
+ mappingContainer.add(passwordLabel);
+ if (AttributableType.USER != ResourceMappingPanel.this.attrType) {
+ passwordLabel.setVisible(false);
+ }
+
+ Collections.sort(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.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.SYNCHRONIZATION || right.getPurpose()
+ == MappingPurpose.NONE)) {
+ compared = -1;
+ } else if (left.getPurpose() == MappingPurpose.SYNCHRONIZATION
+ && right.getPurpose() == MappingPurpose.PROPAGATION) {
+ compared = 1;
+ } else if (left.getPurpose() == MappingPurpose.SYNCHRONIZATION
+ && right.getPurpose() == MappingPurpose.NONE) {
+ compared = -1;
+ } else if (left.getPurpose() == MappingPurpose.NONE
+ && right.getPurpose() != MappingPurpose.NONE) {
+ compared = 1;
+ } else if (left.isAccountid()) {
+ compared = -1;
+ } else if (right.isAccountid()) {
+ compared = 1;
+ } else if (left.isPassword()) {
+ compared = -1;
+ } else if (right.isPassword()) {
+ compared = 1;
+ } else {
+ compared = left.getIntAttrName().compareTo(right.getIntAttrName());
+ }
+ return compared;
+ }
+ });
+
+ mappings = new ListView<MappingItemTO>("mappings", 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);
+ }
+
+ AttributableType entity = null;
+ if (mapItem.getIntMappingType() != null) {
+ entity = mapItem.getIntMappingType().getAttributableType();
+ }
+
+ final List<IntMappingType> attrTypes = new ArrayList<IntMappingType>(getAttributeTypes(entity));
+
+ item.add(new AjaxDecoratedCheckbox("toRemove", new Model<Boolean>(Boolean.FALSE)) {
+
+ private static final long serialVersionUID = 7170946748485726506L;
+
+ @Override
+ protected void onUpdate(final AjaxRequestTarget target) {
+ int index = -1;
+ for (int i = 0; i < getMapping().getItems().size() && index == -1; i++) {
+ if (mapItem.equals(getMapping().getItems().get(i))) {
+ index = i;
+ }
+ }
+
+ if (index != -1) {
+ getMapping().getItems().remove(index);
+ item.getParent().removeAll();
+ target.add(ResourceMappingPanel.this);
+ }
+ }
+
+ @Override
+ protected void updateAjaxAttributes(final AjaxRequestAttributes attributes) {
+ super.updateAjaxAttributes(attributes);
+
+ final AjaxCallListener ajaxCallListener = new AjaxCallListener() {
+
+ private static final long serialVersionUID = 7160235486520935153L;
+
+ @Override
+ public CharSequence getPrecondition(final Component component) {
+ return "if (!confirm('" + getString("confirmDelete") + "')) return false;";
+ }
+ };
+ attributes.getAjaxCallListeners().add(ajaxCallListener);
+ }
+ });
+
+ final AjaxDropDownChoicePanel<String> intAttrNames =
+ new AjaxDropDownChoicePanel<String>("intAttrNames", getString("intAttrNames"),
+ new PropertyModel<String>(mapItem, "intAttrName"), false);
+ intAttrNames.setChoices(schemaNames);
+ intAttrNames.setRequired(true);
+ intAttrNames.setStyleSheet(FIELD_STYLE);
+ intAttrNames.getField().add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+ private static final long serialVersionUID = -1107858522700306810L;
+
+ @Override
+ protected void onUpdate(final AjaxRequestTarget target) {
+ }
+ });
+ item.add(intAttrNames);
+
+ final AjaxDropDownChoicePanel<IntMappingType> intMappingTypes =
+ new AjaxDropDownChoicePanel<IntMappingType>("intMappingTypes",
+ new ResourceModel("intMappingTypes", "intMappingTypes").getObject(),
+ new PropertyModel<IntMappingType>(mapItem, "intMappingType"));
+ intMappingTypes.setRequired(true);
+ intMappingTypes.setChoices(attrTypes);
+ intMappingTypes.setStyleSheet(FIELD_STYLE);
+ item.add(intMappingTypes);
+
+ final AjaxDropDownChoicePanel<AttributableType> entitiesPanel =
+ new AjaxDropDownChoicePanel<AttributableType>("entities",
+ new ResourceModel("entities", "entities").getObject(), new Model<AttributableType>(
+ entity));
+ entitiesPanel.setChoices(attrType == AttributableType.GROUP
+ ? Collections.<AttributableType>singletonList(AttributableType.GROUP)
+ : Arrays.asList(AttributableType.values()));
+ entitiesPanel.setStyleSheet(DEF_FIELD_STYLE);
+ entitiesPanel.getField().add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+ private static final long serialVersionUID = -1107858522700306810L;
+
+ @Override
+ protected void onUpdate(final AjaxRequestTarget target) {
+ attrTypes.clear();
+ attrTypes.addAll(getAttributeTypes(entitiesPanel.getModelObject()));
+ intMappingTypes.setChoices(attrTypes);
+
+ intAttrNames.setChoices(Collections.<String>emptyList());
+
+ target.add(intMappingTypes.getField());
+ target.add(intAttrNames.getField());
+ }
+ });
+ item.add(entitiesPanel);
+
+ final FieldPanel<String> extAttrNames = new AjaxTextFieldPanel("extAttrName",
+ new ResourceModel("extAttrNames", "extAttrNames").getObject(),
+ new PropertyModel<String>(mapItem, "extAttrName"));
+ ((AjaxTextFieldPanel) extAttrNames).setChoices(schemaNames);
+
+ boolean required = false;
+ if (mapItem.isPassword()) {
+ ((AjaxTextFieldPanel) extAttrNames).setModelObject(null);
+ } else {
+ required = true;
+ }
+ extAttrNames.setRequired(required);
+ extAttrNames.setEnabled(required);
+ extAttrNames.setStyleSheet(FIELD_STYLE);
+ item.add(extAttrNames);
+
+ final AjaxTextFieldPanel mandatory = new AjaxTextFieldPanel("mandatoryCondition",
+ new ResourceModel("mandatoryCondition", "mandatoryCondition").getObject(),
+ new PropertyModel<String>(mapItem, "mandatoryCondition"));
+ mandatory.setChoices(Arrays.asList(new String[] { "true", "false" }));
+ mandatory.setStyleSheet(SHORT_FIELD_STYLE);
+ item.add(mandatory);
+
+ final AjaxCheckBoxPanel accountId = new AjaxCheckBoxPanel("accountId",
+ new ResourceModel("accountId", "accountId").getObject(),
+ new PropertyModel<Boolean>(mapItem, "accountid"));
+ accountId.getField().add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+ private static final long serialVersionUID = -1107858522700306810L;
+
+ @Override
+ protected void onUpdate(final AjaxRequestTarget target) {
+ if (accountId.getModelObject()) {
+ mapItem.setMandatoryCondition("true");
+ mandatory.setEnabled(false);
+ } else {
+ mapItem.setMandatoryCondition("false");
+ mandatory.setEnabled(true);
+ }
+ target.add(mandatory);
+ }
+ });
+ item.add(accountId);
+
+ final AjaxCheckBoxPanel password = new AjaxCheckBoxPanel("password",
+ new ResourceModel("password", "password").getObject(),
+ new PropertyModel<Boolean>(mapItem, "password"));
+ password.getField().add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+ private static final long serialVersionUID = -1107858522700306810L;
+
+ @Override
+ protected void onUpdate(final AjaxRequestTarget target) {
+ extAttrNames.setEnabled(!mapItem.isAccountid() && !password.getModelObject());
+ extAttrNames.setModelObject(null);
+ extAttrNames.setRequired(!password.getModelObject());
+ target.add(extAttrNames);
+
+ setAccountId(intMappingTypes.getModelObject(), accountId, password);
+ target.add(accountId);
+ }
+ });
+ item.add(password);
+ if (AttributableType.USER != ResourceMappingPanel.this.attrType) {
+ password.setVisible(false);
+ }
+
+ final WebMarkupContainer purpose = new WebMarkupContainer("purpose");
+ purpose.setOutputMarkupId(Boolean.TRUE);
+
+ final MappingPurposePanel panel = new MappingPurposePanel("purposeActions",
+ new PropertyModel<MappingPurpose>(mapItem, "purpose"), purpose);
+
+ purpose.add(panel);
+
+ item.add(purpose);
+
+ intMappingTypes.getField().add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+ private static final long serialVersionUID = -1107858522700306810L;
+
+ @Override
+ protected void onUpdate(final AjaxRequestTarget target) {
+ setAttrNames(intMappingTypes.getModelObject(), intAttrNames);
+ target.add(intAttrNames);
+
+ setAccountId(intMappingTypes.getModelObject(), accountId, password);
+ target.add(accountId);
+ }
+ });
+
+ setAttrNames(mapItem.getIntMappingType(), intAttrNames);
+ setAccountId(mapItem.getIntMappingType(), accountId, password);
+ }
+ };
+
+ 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) {
+ getMapping().getItems().add(new MappingItemTO());
+ target.add(ResourceMappingPanel.this);
+ }
+ };
+ addMappingBtn.setDefaultFormProcessing(false);
+ addMappingBtn.setEnabled(this.resourceTO.getConnectorId() != null && this.resourceTO.getConnectorId() > 0);
+ mappingContainer.add(addMappingBtn);
+
+ boolean accountLinkEnabled = false;
+ if (getMapping().getAccountLink() != null) {
+ accountLinkEnabled = true;
+ }
+ accountLinkCheckbox = new AjaxCheckBoxPanel("accountLinkCheckbox",
+ new ResourceModel("accountLinkCheckbox", "accountLinkCheckbox").getObject(),
+ new Model<Boolean>(Boolean.valueOf(accountLinkEnabled)));
+ accountLinkCheckbox.setEnabled(true);
+
+ accountLinkContainer.add(accountLinkCheckbox);
+
+ final AjaxTextFieldPanel accountLink = new AjaxTextFieldPanel("accountLink",
+ new ResourceModel("accountLink", "accountLink").getObject(),
+ new PropertyModel<String>(getMapping(), "accountLink"));
+ accountLink.setEnabled(accountLinkEnabled);
+ accountLinkContainer.add(accountLink);
+
+ accountLinkCheckbox.getField().add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+ private static final long serialVersionUID = -1107858522700306810L;
+
+ @Override
+ protected void onUpdate(final AjaxRequestTarget target) {
+ if (accountLinkCheckbox.getModelObject()) {
+ accountLink.setEnabled(Boolean.TRUE);
+ accountLink.setModelObject("");
+ } else {
+ accountLink.setEnabled(Boolean.FALSE);
+ accountLink.setModelObject("");
+ }
+
+ target.add(accountLink);
+ }
+ });
+ }
+
+ private List<String> getSchemaNames(final Long connectorId, final Set<ConnConfProperty> conf) {
+ final ConnInstanceTO connInstanceTO = new ConnInstanceTO();
+ connInstanceTO.setKey(connectorId);
+ connInstanceTO.getConfiguration().addAll(conf);
+
+ return connRestClient.getSchemaNames(connInstanceTO);
+ }
+
+ private void setEnabled() {
+ final ConnInstanceTO connInstanceTO = new ConnInstanceTO();
+ connInstanceTO.setKey(this.resourceTO.getConnectorId());
+ connInstanceTO.getConfiguration().addAll(this.resourceTO.getConnConfProperties());
+
+ List<ConnIdObjectClassTO> objectClasses = connRestClient.getSupportedObjectClasses(connInstanceTO);
+
+ boolean enabled = objectClasses.isEmpty()
+ || (AttributableType.USER == attrType && objectClasses.contains(ConnIdObjectClassTO.ACCOUNT))
+ || (AttributableType.GROUP == attrType && objectClasses.contains(ConnIdObjectClassTO.GROUP));
+ this.mappingContainer.setEnabled(enabled);
+ this.mappingContainer.setVisible(enabled);
+ this.accountLinkContainer.setEnabled(enabled);
+ this.accountLinkContainer.setVisible(enabled);
+
+ if (!enabled) {
+ getMapping().getItems().clear();
+ getMapping().setAccountLink(null);
+ if (this.accountLinkCheckbox != null) {
+ this.accountLinkCheckbox.setModelObject(null);
+ }
+ }
+ }
+
+ @Override
+ public void onEvent(final IEvent<?> event) {
+ if (event.getPayload() instanceof ConnConfModEvent) {
+ final AjaxRequestTarget target = ((ConnConfModEvent) event.getPayload()).getTarget();
+
+ final List<ConnConfProperty> conf = ((ConnConfModEvent) event.getPayload()).getConfiguration();
+
+ mappings.removeAll();
+
+ addMappingBtn.setEnabled(resourceTO.getConnectorId() != null && resourceTO.getConnectorId() > 0);
+
+ schemaNames.clear();
+ schemaNames.addAll(getSchemaNames(resourceTO.getConnectorId(), new HashSet<ConnConfProperty>(conf)));
+
+ setEnabled();
+
+ target.add(this);
+ }
+ }
+
+ /**
+ * 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 IntMappingType type, final AjaxDropDownChoicePanel<String> toBeUpdated) {
+ toBeUpdated.setRequired(true);
+ toBeUpdated.setEnabled(true);
+
+ if (type == null || type.getAttributableType() == null) {
+ toBeUpdated.setChoices(Collections.<String>emptyList());
+ } else {
+ switch (type) {
+ // user attribute names
+ case UserPlainSchema:
+ case GroupPlainSchema:
+ case MembershipPlainSchema:
+ toBeUpdated.setChoices(schemaRestClient.getPlainSchemaNames(type.getAttributableType()));
+ break;
+
+ case UserDerivedSchema:
+ case GroupDerivedSchema:
+ case MembershipDerivedSchema:
+ toBeUpdated.setChoices(schemaRestClient.getDerSchemaNames(type.getAttributableType()));
+ break;
+
+ case UserVirtualSchema:
+ case GroupVirtualSchema:
+ case MembershipVirtualSchema:
+ toBeUpdated.setChoices(schemaRestClient.getVirSchemaNames(type.getAttributableType()));
+ break;
+
+ case UserId:
+ case Password:
+ case Username:
+ case GroupId:
+ case GroupName:
+ default:
+ toBeUpdated.setRequired(false);
+ toBeUpdated.setEnabled(false);
+ toBeUpdated.setChoices(Collections.<String>emptyList());
+ }
+ }
+ }
+
+ /**
+ * Enable/Disable accountId checkbox.
+ *
+ * @param type attribute type.
+ * @param accountId accountId checkbox.
+ * @param password password checkbox.
+ */
+ private void setAccountId(final IntMappingType type, final AjaxCheckBoxPanel accountId,
+ final AjaxCheckBoxPanel password) {
+
+ if (type != null && type.getAttributableType() != null) {
+ switch (type) {
+ case UserVirtualSchema:
+ case GroupVirtualSchema:
+ case MembershipVirtualSchema:
+ // Virtual accountId is not permitted
+ case Password:
+ // AccountId cannot be derived from password.
+ accountId.setReadOnly(true);
+ accountId.setModelObject(false);
+ break;
+
+ default:
+ if (password.getModelObject()) {
+ accountId.setReadOnly(true);
+ accountId.setModelObject(false);
+ } else {
+ accountId.setReadOnly(false);
+ }
+ }
+ }
+ }
+
+ /**
+ * Get all attribute types from a selected attribute type.
+ *
+ * @param entity entity.
+ * @return all attribute types.
+ */
+ private List<IntMappingType> getAttributeTypes(final AttributableType entity) {
+ final List<IntMappingType> res = new ArrayList<>();
+
+ if (entity != null) {
+ res.addAll(IntMappingType.getAttributeTypes(AttributableType.valueOf(entity.name())));
+ }
+
+ return res;
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/34ec6712/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceSecurityPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceSecurityPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceSecurityPanel.java
new file mode 100644
index 0000000..15d2fe8
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceSecurityPanel.java
@@ -0,0 +1,186 @@
+/*
+ * 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.panels;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.syncope.client.console.rest.PolicyRestClient;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxDropDownChoicePanel;
+import org.apache.syncope.common.lib.to.AbstractPolicyTO;
+import org.apache.syncope.common.lib.to.ResourceTO;
+import org.apache.syncope.common.lib.types.PolicyType;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.form.ChoiceRenderer;
+import org.apache.wicket.markup.html.form.DropDownChoice;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.LoadableDetachableModel;
+import org.apache.wicket.model.PropertyModel;
+import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.spring.injection.annot.SpringBean;
+
+public class ResourceSecurityPanel extends Panel {
+
+ private static final long serialVersionUID = -7982691107029848579L;
+
+ @SpringBean
+ private PolicyRestClient policyRestClient;
+
+ private IModel<Map<Long, String>> passwordPolicies = null;
+
+ private IModel<Map<Long, String>> accountPolicies = null;
+
+ private IModel<Map<Long, String>> syncPolicies = null;
+
+ public ResourceSecurityPanel(final String id, final ResourceTO resourceTO) {
+
+ super(id);
+
+ setOutputMarkupId(true);
+
+ passwordPolicies = new LoadableDetachableModel<Map<Long, String>>() {
+
+ private static final long serialVersionUID = 5275935387613157437L;
+
+ @Override
+ protected Map<Long, String> load() {
+ Map<Long, String> res = new HashMap<>();
+ for (AbstractPolicyTO policyTO : policyRestClient.getPolicies(PolicyType.PASSWORD)) {
+ res.put(policyTO.getKey(), policyTO.getDescription());
+ }
+ return res;
+ }
+ };
+
+ accountPolicies = new LoadableDetachableModel<Map<Long, String>>() {
+
+ private static final long serialVersionUID = -2012833443695917883L;
+
+ @Override
+ protected Map<Long, String> load() {
+ Map<Long, String> res = new HashMap<>();
+ for (AbstractPolicyTO policyTO : policyRestClient.getPolicies(PolicyType.ACCOUNT)) {
+ res.put(policyTO.getKey(), policyTO.getDescription());
+ }
+ return res;
+ }
+ };
+
+ syncPolicies = new LoadableDetachableModel<Map<Long, String>>() {
+
+ private static final long serialVersionUID = -2012833443695917883L;
+
+ @Override
+ protected Map<Long, String> load() {
+ Map<Long, String> res = new HashMap<>();
+ for (AbstractPolicyTO policyTO : policyRestClient.getPolicies(PolicyType.SYNC)) {
+ res.put(policyTO.getKey(), policyTO.getDescription());
+ }
+ return res;
+ }
+ };
+
+ final WebMarkupContainer securityContainer = new WebMarkupContainer("security");
+
+ securityContainer.setOutputMarkupId(true);
+ add(securityContainer);
+
+ // -------------------------------
+ // Password policy specification
+ // -------------------------------
+ final AjaxDropDownChoicePanel<Long> passwordPolicy = new AjaxDropDownChoicePanel<Long>("passwordPolicy",
+ new ResourceModel("passwordPolicy", "passwordPolicy").getObject(), new PropertyModel<Long>(resourceTO,
+ "passwordPolicy"));
+
+ passwordPolicy.setChoiceRenderer(new PolicyRenderer(PolicyType.PASSWORD));
+
+ passwordPolicy.setChoices(new ArrayList<>(passwordPolicies.getObject().keySet()));
+
+ ((DropDownChoice<?>) passwordPolicy.getField()).setNullValid(true);
+
+ securityContainer.add(passwordPolicy);
+ // -------------------------------
+
+ // -------------------------------
+ // Account policy specification
+ // -------------------------------
+ final AjaxDropDownChoicePanel<Long> accountPolicy = new AjaxDropDownChoicePanel<Long>("accountPolicy",
+ new ResourceModel("accountPolicy", "accountPolicy").getObject(), new PropertyModel<Long>(resourceTO,
+ "accountPolicy"));
+
+ accountPolicy.setChoiceRenderer(new PolicyRenderer(PolicyType.ACCOUNT));
+
+ accountPolicy.setChoices(new ArrayList<Long>(accountPolicies.getObject().keySet()));
+
+ ((DropDownChoice<?>) accountPolicy.getField()).setNullValid(true);
+
+ securityContainer.add(accountPolicy);
+ // -------------------------------
+
+ // -------------------------------
+ // Sync policy specification
+ // -------------------------------
+ final AjaxDropDownChoicePanel<Long> syncPolicy = new AjaxDropDownChoicePanel<Long>("syncPolicy",
+ new ResourceModel("syncPolicy", "syncPolicy").getObject(), new PropertyModel<Long>(resourceTO,
+ "syncPolicy"));
+
+ syncPolicy.setChoiceRenderer(new PolicyRenderer(PolicyType.SYNC));
+
+ syncPolicy.setChoices(new ArrayList<Long>(syncPolicies.getObject().keySet()));
+
+ ((DropDownChoice<?>) syncPolicy.getField()).setNullValid(true);
+
+ securityContainer.add(syncPolicy);
+ // -------------------------------
+ }
+
+ private class PolicyRenderer extends ChoiceRenderer<Long> {
+
+ private static final long serialVersionUID = 8060500161321947000L;
+
+ private PolicyType type;
+
+ public PolicyRenderer(final PolicyType type) {
+ super();
+ this.type = type;
+ }
+
+ @Override
+ public Object getDisplayValue(final Long object) {
+ switch (type) {
+ case ACCOUNT:
+ return accountPolicies.getObject().get(object);
+ case PASSWORD:
+ return passwordPolicies.getObject().get(object);
+ case SYNC:
+ return syncPolicies.getObject().get(object);
+ default:
+ return "";
+ }
+ }
+
+ @Override
+ public String getIdValue(final Long object, final int index) {
+ return String.valueOf(object != null
+ ? object
+ : 0L);
+ }
+ };
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/34ec6712/client/console/src/main/java/org/apache/syncope/client/console/panels/Resources.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/Resources.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/Resources.java
new file mode 100644
index 0000000..755b2d1
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/Resources.java
@@ -0,0 +1,485 @@
+/*
+ * 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.panels;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import org.apache.syncope.client.console.PreferenceManager;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.commons.SortableDataProviderComparator;
+import org.apache.syncope.client.console.pages.BasePage;
+import org.apache.syncope.client.console.pages.ConnectorModalPage;
+import org.apache.syncope.client.console.pages.ProvisioningModalPage;
+import org.apache.syncope.client.console.pages.ResourceModalPage;
+import org.apache.syncope.client.console.rest.ConnectorRestClient;
+import org.apache.syncope.client.console.rest.ResourceRestClient;
+import org.apache.syncope.client.console.wicket.ajax.markup.html.ClearIndicatingAjaxLink;
+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.LinkPanel;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.ResourceTO;
+import org.apache.syncope.common.lib.to.GroupTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.Entitlement;
+import org.apache.wicket.AttributeModifier;
+import org.apache.wicket.Page;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
+import org.apache.wicket.ajax.markup.html.AjaxLink;
+import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
+import org.apache.wicket.event.IEvent;
+import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
+import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
+import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.ISortableDataProvider;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
+import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.DropDownChoice;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.model.AbstractReadOnlyModel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.PropertyModel;
+import org.apache.wicket.model.StringResourceModel;
+import org.apache.wicket.spring.injection.annot.SpringBean;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Resources WebPage.
+ */
+public class Resources extends Panel {
+
+ private static final long serialVersionUID = -3789252860990261728L;
+
+ protected static final Logger LOG = LoggerFactory.getLogger(Resources.class);
+
+ private static final int WIN_HEIGHT = 600;
+
+ private static final int WIN_WIDTH = 1100;
+
+ @SpringBean
+ private PreferenceManager prefMan;
+
+ @SpringBean
+ private ResourceRestClient resourceRestClient;
+
+ private final ModalWindow createResourceWin;
+
+ private final ModalWindow editResourceWin;
+
+ private final int resourcePaginatorRows;
+
+ private WebMarkupContainer resourceContainer;
+
+ private final ModalWindow editConnectorWin;
+
+ /**
+ * Modal window to be used for user status management.
+ */
+ protected final ModalWindow statusmodal = new ModalWindow("statusModal");
+
+ /**
+ * Schemas to be shown modal window height.
+ */
+ private static final int STATUS_MODAL_WIN_HEIGHT = 500;
+
+ /**
+ * Schemas to be shown modal window width.
+ */
+ private static final int STATUS_MODAL_WIN_WIDTH = 700;
+
+ @SpringBean
+ private ConnectorRestClient connectorRestClient;
+
+ private final PageReference pageRef;
+
+ public Resources(final String id, final PageReference pageRef) {
+ super(id);
+ this.pageRef = pageRef;
+
+ createResourceWin = new ModalWindow("createResourceWin");
+ add(createResourceWin);
+
+ editResourceWin = new ModalWindow("editResourceWin");
+ add(editResourceWin);
+
+ editConnectorWin = new ModalWindow("editConnectorWin");
+ add(editConnectorWin);
+
+ statusmodal.setCssClassName(ModalWindow.CSS_CLASS_GRAY);
+ statusmodal.setInitialHeight(STATUS_MODAL_WIN_HEIGHT);
+ statusmodal.setInitialWidth(STATUS_MODAL_WIN_WIDTH);
+ statusmodal.setCookieName("status-modal");
+ add(statusmodal);
+
+ resourcePaginatorRows = prefMan.getPaginatorRows(getRequest(), Constants.PREF_RESOURCES_PAGINATOR_ROWS);
+
+ setupResources();
+ }
+
+ private void setupResources() {
+ List<IColumn<ResourceTO, String>> columns = new ArrayList<>();
+
+ columns.add(new PropertyColumn<ResourceTO, String>(new StringResourceModel("key", this, null), "key", "key"));
+
+ columns.add(new AbstractColumn<ResourceTO, String>(
+ new StringResourceModel("connector", this, null, "connector")) {
+
+ private static final long serialVersionUID = 8263694778917279290L;
+
+ @Override
+ public void populateItem(final Item<ICellPopulator<ResourceTO>> cellItem, final String componentId,
+ final IModel<ResourceTO> rowModel) {
+
+ final AjaxLink<String> editLink = new ClearIndicatingAjaxLink<String>("link", pageRef) {
+
+ private static final long serialVersionUID = -7978723352517770644L;
+
+ @Override
+ protected void onClickInternal(final AjaxRequestTarget target) {
+
+ editConnectorWin.setPageCreator(new ModalWindow.PageCreator() {
+
+ private static final long serialVersionUID = -7834632442532690940L;
+
+ @Override
+ public Page createPage() {
+ return new ConnectorModalPage(Resources.this.pageRef,
+ editConnectorWin,
+ connectorRestClient.read(rowModel.getObject().getConnectorId()));
+ }
+ });
+
+ editConnectorWin.show(target);
+ }
+ };
+ editLink.add(new Label("linkTitle", rowModel.getObject().getConnectorDisplayName()));
+
+ LinkPanel editConnPanel = new LinkPanel(componentId);
+ editConnPanel.add(editLink);
+
+ cellItem.add(editConnPanel);
+
+ MetaDataRoleAuthorizationStrategy.authorize(editConnPanel, ENABLE, Entitlement.CONNECTOR_READ);
+ }
+ });
+
+ columns.add(new AbstractColumn<ResourceTO, String>(
+ new StringResourceModel("propagationPrimary", this, null)) {
+
+ private static final long serialVersionUID = -3503023501954863131L;
+
+ @Override
+ public void populateItem(final Item<ICellPopulator<ResourceTO>> item,
+ final String componentId, final IModel<ResourceTO> model) {
+
+ item.add(new Label(componentId, ""));
+ item.add(new AttributeModifier("class", new Model<>(
+ Boolean.toString(model.getObject().isPropagationPrimary()))));
+ }
+
+ @Override
+ public String getCssClass() {
+ return "narrowcolumn";
+ }
+ });
+
+ columns.add(new PropertyColumn<ResourceTO, String>(new StringResourceModel(
+ "propagationPriority", this, null), "propagationPriority", "propagationPriority") {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public String getCssClass() {
+ return "narrowcolumn";
+ }
+ });
+
+ columns.add(new AbstractColumn<ResourceTO, String>(new StringResourceModel("actions", this, null, "")) {
+
+ private static final long serialVersionUID = 2054811145491901166L;
+
+ @Override
+ public String getCssClass() {
+ return "action";
+ }
+
+ @Override
+ public void populateItem(final Item<ICellPopulator<ResourceTO>> cellItem, final String componentId,
+ final IModel<ResourceTO> model) {
+
+ final ResourceTO resourceTO = model.getObject();
+
+ final ActionLinksPanel panel = new ActionLinksPanel(componentId, model, pageRef);
+ panel.add(new ActionLink() {
+
+ private static final long serialVersionUID = -3722207913631435501L;
+
+ @Override
+ public void onClick(final AjaxRequestTarget target) {
+ statusmodal.setPageCreator(new ModalWindow.PageCreator() {
+
+ private static final long serialVersionUID = -7834632442532690940L;
+
+ @Override
+ public Page createPage() {
+ return new ProvisioningModalPage<>(
+ pageRef, statusmodal, model.getObject(), UserTO.class);
+ }
+ });
+
+ statusmodal.show(target);
+ }
+ }, ActionLink.ActionType.MANAGE_USERS, "Resources");
+
+ panel.add(new ActionLink() {
+
+ private static final long serialVersionUID = -3722207913631435501L;
+
+ @Override
+ public void onClick(final AjaxRequestTarget target) {
+
+ statusmodal.setPageCreator(new ModalWindow.PageCreator() {
+
+ private static final long serialVersionUID = -7834632442532690940L;
+
+ @Override
+ public Page createPage() {
+ return new ProvisioningModalPage<>(
+ pageRef, statusmodal, model.getObject(), GroupTO.class);
+ }
+ });
+
+ statusmodal.show(target);
+ }
+ }, ActionLink.ActionType.MANAGE_GROUPS, "Resources");
+
+ panel.add(new ActionLink() {
+
+ private static final long serialVersionUID = -3722207913631435501L;
+
+ @Override
+ public void onClick(final AjaxRequestTarget target) {
+ resourceTO.setUsyncToken(null);
+ resourceTO.setRsyncToken(null);
+ try {
+ resourceRestClient.update(resourceTO);
+ info(getString(Constants.OPERATION_SUCCEEDED));
+ } catch (SyncopeClientException e) {
+ error(getString(Constants.ERROR) + ":" + e.getMessage());
+
+ LOG.error("While resetting sync token from " + resourceTO.getKey(), e);
+ }
+
+ ((BasePage) pageRef.getPage()).getFeedbackPanel().refresh(target);
+ target.add(resourceContainer);
+ }
+ }, ActionLink.ActionType.RESET, "Resources");
+
+ panel.add(new ActionLink() {
+
+ private static final long serialVersionUID = -3722207913631435501L;
+
+ @Override
+ public void onClick(final AjaxRequestTarget target) {
+ editResourceWin.setPageCreator(new ModalWindow.PageCreator() {
+
+ private static final long serialVersionUID = -7834632442532690940L;
+
+ @Override
+ public Page createPage() {
+ return new ResourceModalPage(pageRef, editResourceWin, resourceTO, false);
+ }
+ });
+
+ editResourceWin.show(target);
+ }
+ }, ActionLink.ActionType.EDIT, "Resources");
+
+ panel.add(new ActionLink() {
+
+ private static final long serialVersionUID = -3722207913631435501L;
+
+ @Override
+ public void onClick(final AjaxRequestTarget target) {
+ try {
+ resourceRestClient.delete(resourceTO.getKey());
+ info(getString(Constants.OPERATION_SUCCEEDED));
+ } catch (SyncopeClientException e) {
+ error(getString(Constants.ERROR) + ": " + e.getMessage());
+
+ LOG.error("While deleting resource " + resourceTO.getKey(), e);
+ }
+
+ ((BasePage) pageRef.getPage()).getFeedbackPanel().refresh(target);
+ target.add(resourceContainer);
+ }
+ }, ActionLink.ActionType.DELETE, "Resources");
+
+ cellItem.add(panel);
+ }
+ });
+
+ final AjaxDataTablePanel<ResourceTO, String> table = new AjaxDataTablePanel<>(
+ "resourceDatatable",
+ columns,
+ (ISortableDataProvider<ResourceTO, String>) new ResourcesProvider(),
+ resourcePaginatorRows,
+ Arrays.asList(new ActionLink.ActionType[] { ActionLink.ActionType.DELETE }),
+ resourceRestClient,
+ "key",
+ "Resources",
+ pageRef);
+
+ resourceContainer = new WebMarkupContainer("resourceContainer");
+ resourceContainer.add(table);
+ resourceContainer.setOutputMarkupId(true);
+
+ add(resourceContainer);
+
+ ((BasePage) pageRef.getPage()).setWindowClosedCallback(createResourceWin, resourceContainer);
+ ((BasePage) pageRef.getPage()).setWindowClosedCallback(editResourceWin, resourceContainer);
+
+ createResourceWin.setCssClassName(ModalWindow.CSS_CLASS_GRAY);
+ createResourceWin.setInitialHeight(WIN_HEIGHT);
+ createResourceWin.setInitialWidth(WIN_WIDTH);
+ createResourceWin.setCookieName("create-res-modal");
+
+ editResourceWin.setCssClassName(ModalWindow.CSS_CLASS_GRAY);
+ editResourceWin.setInitialHeight(WIN_HEIGHT);
+ editResourceWin.setInitialWidth(WIN_WIDTH);
+ editResourceWin.setCookieName("edit-res-modal");
+
+ editConnectorWin.setCssClassName(ModalWindow.CSS_CLASS_GRAY);
+ editConnectorWin.setInitialHeight(WIN_HEIGHT);
+ editConnectorWin.setInitialWidth(WIN_WIDTH);
+ editConnectorWin.setCookieName("edit-conn-modal");
+
+ AjaxLink<Void> createResourceLink
+ = new ClearIndicatingAjaxLink<Void>("createResourceLink", pageRef) {
+
+ private static final long serialVersionUID = -7978723352517770644L;
+
+ @Override
+ protected void onClickInternal(final AjaxRequestTarget target) {
+ createResourceWin.setPageCreator(new ModalWindow.PageCreator() {
+
+ private static final long serialVersionUID = -7834632442532690940L;
+
+ @Override
+ public Page createPage() {
+ final ResourceModalPage windows = new ResourceModalPage(Resources.this.pageRef,
+ editResourceWin, new ResourceTO(), true);
+ return windows;
+ }
+ });
+
+ createResourceWin.show(target);
+ }
+ };
+
+ MetaDataRoleAuthorizationStrategy.authorize(createResourceLink, ENABLE, Entitlement.RESOURCE_CREATE);
+
+ add(createResourceLink);
+
+ @SuppressWarnings("rawtypes")
+ final Form paginatorForm = new Form("resourcePaginatorForm");
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ final DropDownChoice rowsChooser = new DropDownChoice("rowsChooser", new PropertyModel(this,
+ "resourcePaginatorRows"), prefMan.getPaginatorChoices());
+
+ rowsChooser.add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+ private static final long serialVersionUID = -1107858522700306810L;
+
+ @Override
+ protected void onUpdate(final AjaxRequestTarget target) {
+ prefMan.set(getRequest(), getResponse(), Constants.PREF_RESOURCES_PAGINATOR_ROWS,
+ String.valueOf(resourcePaginatorRows));
+
+ table.setItemsPerPage(resourcePaginatorRows);
+ target.add(resourceContainer);
+ }
+ });
+
+ paginatorForm.add(rowsChooser);
+ add(paginatorForm);
+ }
+
+ class ResourcesProvider extends SortableDataProvider<ResourceTO, String> {
+
+ private static final long serialVersionUID = -9055916672926643975L;
+
+ private final SortableDataProviderComparator<ResourceTO> comparator;
+
+ public ResourcesProvider() {
+ super();
+ //Default sorting
+ setSort("key", SortOrder.ASCENDING);
+ comparator = new SortableDataProviderComparator<>(this);
+ }
+
+ @Override
+ public Iterator<ResourceTO> iterator(final long first, final long count) {
+ List<ResourceTO> list = resourceRestClient.getAll();
+
+ Collections.sort(list, comparator);
+
+ return list.subList((int) first, (int) first + (int) count).iterator();
+ }
+
+ @Override
+ public long size() {
+ return resourceRestClient.getAll().size();
+ }
+
+ @Override
+ public IModel<ResourceTO> model(final ResourceTO resource) {
+ return new AbstractReadOnlyModel<ResourceTO>() {
+
+ private static final long serialVersionUID = 8952474152465381634L;
+
+ @Override
+ public ResourceTO getObject() {
+ return resource;
+ }
+ };
+ }
+ }
+
+ @Override
+ public void onEvent(final IEvent<?> event) {
+ if (event.getPayload() instanceof AbstractSearchResultPanel.EventDataWrapper) {
+ ((AbstractSearchResultPanel.EventDataWrapper) event.getPayload()).getTarget().add(resourceContainer);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/34ec6712/client/console/src/main/java/org/apache/syncope/client/console/rest/ConnectorRestClient.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/rest/ConnectorRestClient.java b/client/console/src/main/java/org/apache/syncope/client/console/rest/ConnectorRestClient.java
index a093dbd..d5c133b 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/rest/ConnectorRestClient.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/rest/ConnectorRestClient.java
@@ -125,14 +125,14 @@ public class ConnectorRestClient extends BaseRestClient {
}
private Set<ConnConfProperty> filterProperties(final Set<ConnConfProperty> properties) {
- Set<ConnConfProperty> newProperties = new HashSet<ConnConfProperty>();
+ Set<ConnConfProperty> newProperties = new HashSet<>();
for (ConnConfProperty property : properties) {
ConnConfProperty prop = new ConnConfProperty();
prop.setSchema(property.getSchema());
prop.setOverridable(property.isOverridable());
- final List<Object> parsed = new ArrayList<Object>();
+ final List<Object> parsed = new ArrayList<>();
if (property.getValues() != null) {
for (Object obj : property.getValues()) {
if (obj != null && !obj.toString().isEmpty()) {
@@ -179,7 +179,7 @@ public class ConnectorRestClient extends BaseRestClient {
}
public List<String> getSchemaNames(final ConnInstanceTO connectorTO) {
- List<String> schemaNames = new ArrayList<String>();
+ List<String> schemaNames = new ArrayList<>();
try {
List<PlainSchemaTO> response = getService(ConnectorService.class).
getSchemaNames(connectorTO.getKey(), connectorTO, false);
http://git-wip-us.apache.org/repos/asf/syncope/blob/34ec6712/client/console/src/main/java/org/apache/syncope/client/console/rest/RealmRestClient.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/rest/RealmRestClient.java b/client/console/src/main/java/org/apache/syncope/client/console/rest/RealmRestClient.java
new file mode 100644
index 0000000..06cba4d
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/rest/RealmRestClient.java
@@ -0,0 +1,38 @@
+/*
+ * 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.rest;
+
+import java.util.List;
+
+import org.apache.syncope.common.lib.to.RealmTO;
+import org.apache.syncope.common.rest.api.service.RealmService;
+import org.springframework.stereotype.Component;
+
+/**
+ * Console client for invoking Rest Group's services.
+ */
+@Component
+public class RealmRestClient extends BaseRestClient {
+
+ private static final long serialVersionUID = -8549081557283519638L;
+
+ public List<RealmTO> list() {
+ return getService(RealmService.class).list();
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/34ec6712/client/console/src/main/java/org/apache/syncope/client/console/topology/Topology.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/topology/Topology.java b/client/console/src/main/java/org/apache/syncope/client/console/topology/Topology.java
new file mode 100644
index 0000000..0b49f12
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/topology/Topology.java
@@ -0,0 +1,521 @@
+/*
+ * 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.topology;
+
+import static org.apache.syncope.client.console.topology.TopologyNode.Status.FAILURE;
+import static org.apache.syncope.client.console.topology.TopologyNode.Status.REACHABLE;
+import static org.apache.syncope.client.console.topology.TopologyNode.Status.UNKNOWN;
+import static org.apache.syncope.client.console.topology.TopologyNode.Status.UNREACHABLE;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.pages.BasePage;
+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.common.lib.to.ConnInstanceTO;
+import org.apache.syncope.common.lib.to.ResourceTO;
+import org.apache.syncope.common.lib.types.Entitlement;
+import org.apache.syncope.common.rest.api.service.SyncopeService;
+import org.apache.wicket.Component;
+import org.apache.wicket.ajax.AbstractAjaxTimerBehavior;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.behavior.Behavior;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+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.util.time.Duration;
+
+public class Topology extends BasePage {
+
+ private static final long serialVersionUID = -1100228004207271272L;
+
+ private static final String CONNECTOR_SERVER_LOCATION_PREFIX = "connid://";
+
+ private final int origX = 3100;
+
+ private final int origY = 2800;
+
+ private final LoadableDetachableModel<List<ResourceTO>> resModel
+ = new LoadableDetachableModel<List<ResourceTO>>() {
+
+ private static final long serialVersionUID = 5275935387613157431L;
+
+ @Override
+ protected List<ResourceTO> load() {
+ final List<ResourceTO> result = resourceRestClient.getAll();
+ return result;
+ }
+ };
+
+ private final LoadableDetachableModel<Pair<List<ConnInstanceTO>, List<ConnInstanceTO>>> connModel
+ = new LoadableDetachableModel<Pair<List<ConnInstanceTO>, List<ConnInstanceTO>>>() {
+
+ private static final long serialVersionUID = 5275935387613157432L;
+
+ @Override
+ protected Pair<List<ConnInstanceTO>, List<ConnInstanceTO>> load() {
+ final List<ConnInstanceTO> level1 = new ArrayList<>();
+ final List<ConnInstanceTO> level2 = new ArrayList<>();
+
+ for (ConnInstanceTO conn : connectorRestClient.getAllConnectors()) {
+ if (conn.getLocation().startsWith(CONNECTOR_SERVER_LOCATION_PREFIX)) {
+ level2.add(conn);
+ } else {
+ level1.add(conn);
+ }
+ }
+
+ return Pair.of(level1, level2);
+ }
+ };
+
+ private final LoadableDetachableModel<List<URI>> csModel = new LoadableDetachableModel<List<URI>>() {
+
+ private static final long serialVersionUID = 5275935387613157433L;
+
+ @Override
+ protected List<URI> load() {
+ final List<URI> locations = new ArrayList<>();
+
+ for (String location : SyncopeConsoleSession.get().getSyncopeTO().getConnIdLocations()) {
+ if (location.startsWith(CONNECTOR_SERVER_LOCATION_PREFIX)) {
+ locations.add(URI.create(location));
+ }
+ }
+
+ return locations;
+ }
+ };
+
+ public Topology() {
+ // -----------------------------------------
+ // Add Zoom panel
+ // -----------------------------------------
+ final ActionLinksPanel zoomActionPanel = new ActionLinksPanel("zoom", new Model<String>(), getPageReference());
+ add(zoomActionPanel);
+
+ zoomActionPanel.add(new ActionLink() {
+
+ private static final long serialVersionUID = -3722207913631435501L;
+
+ @Override
+ public void onClick(final AjaxRequestTarget target) {
+ target.appendJavaScript("zoomIn($('#drawing')[0]);");
+ }
+
+ }, ActionLink.ActionType.ZOOM_IN, Entitlement.RESOURCE_LIST).add(new ActionLink() {
+
+ private static final long serialVersionUID = -3722207913631435501L;
+
+ @Override
+ public void onClick(final AjaxRequestTarget target) {
+ target.appendJavaScript("zoomOut($('#drawing')[0]);");
+ }
+ }, ActionLink.ActionType.ZOOM_OUT, Entitlement.RESOURCE_LIST);
+ // -----------------------------------------
+
+ final WebMarkupContainer jsPlace = new WebMarkupContainer("jsPlace");
+
+ // -----------------------------------------
+ // Add Syncope (root topologynode)
+ // -----------------------------------------
+ final TopologyNode syncopeTopologyNode = new TopologyNode("Syncope", "Syncope", TopologyNode.Kind.SYNCOPE);
+ syncopeTopologyNode.setX(origX);
+ syncopeTopologyNode.setY(origY);
+
+ final URI uri = WebClient.client(SyncopeConsoleSession.get().getService(SyncopeService.class)).getBaseURI();
+ syncopeTopologyNode.setHost(uri.getHost());
+ syncopeTopologyNode.setPort(uri.getPort());
+
+ add(topologyNodePanel("syncope", syncopeTopologyNode, null, false));
+
+ final Map<String, Map<String, TopologyNode>> connections = new HashMap<>();
+ final Map<String, TopologyNode> syncopeConnections = new HashMap<>();
+ connections.put(syncopeTopologyNode.getDisplayName(), syncopeConnections);
+
+ // required to retrieve parent positions
+ final Map<String, TopologyNode> servers = new HashMap<>();
+ final Map<String, TopologyNode> connectors = new HashMap<>();
+ // -----------------------------------------
+
+ // -----------------------------------------
+ // Add Connector Servers
+ // -----------------------------------------
+ final ListView<URI> connectorServers = new ListView<URI>("connectorServers", csModel) {
+
+ private static final long serialVersionUID = 6978621871488360380L;
+
+ private final int size = csModel.getObject().size() + 1;
+
+ @Override
+ protected void populateItem(final ListItem<URI> item) {
+ int kx = size >= 4 ? 800 : (200 * size);
+
+ int x = (int) Math.round(origX + kx * Math.cos(Math.PI + Math.PI * (item.getIndex() + 1) / size));
+ int y = (int) Math.round(origY + 100 * Math.sin(Math.PI + Math.PI * (item.getIndex() + 1) / size));
+
+ final URI location = item.getModelObject();
+ final String url = location.toASCIIString();
+
+ final TopologyNode topologynode = new TopologyNode(url, url, TopologyNode.Kind.CONNECTOR_SERVER);
+
+ topologynode.setHost(location.getHost());
+ topologynode.setPort(location.getPort());
+ topologynode.setX(x);
+ topologynode.setY(y);
+
+ servers.put(topologynode.getDisplayName(), topologynode);
+
+ item.add(topologyNodePanel("cs", topologynode, syncopeTopologyNode, false));
+
+ syncopeConnections.put(url, topologynode);
+ connections.put(url, new HashMap<String, TopologyNode>());
+ }
+ };
+
+ connectorServers.setOutputMarkupId(true);
+ add(connectorServers);
+ // -----------------------------------------
+
+ // -----------------------------------------
+ // Add Connector Intances (first level)
+ // -----------------------------------------
+ final ListView<ConnInstanceTO> conn1
+ = new ListView<ConnInstanceTO>("conn1", connModel.getObject().getLeft()) {
+
+ private static final long serialVersionUID = 6978621871488360381L;
+
+ private final int size = connModel.getObject().getLeft().size() + 1;
+
+ @Override
+ protected void populateItem(final ListItem<ConnInstanceTO> item) {
+ int kx = size >= 6 ? 800 : (130 * size);
+
+ int x = (int) Math.round(origX + kx * Math.cos(Math.PI * (item.getIndex() + 1) / size));
+ int y = (int) Math.round(origY + 100 * Math.sin(Math.PI * (item.getIndex() + 1) / size));
+
+ final ConnInstanceTO conn = item.getModelObject();
+ final TopologyNode topologynode = new TopologyNode(
+ Long.valueOf(conn.getKey()), conn.getDisplayName(), TopologyNode.Kind.CONNECTOR);
+ topologynode.setConnectinDisplayName(conn.getBundleName());
+ topologynode.setX(x);
+ topologynode.setY(y);
+
+ connectors.put(topologynode.getDisplayName(), topologynode);
+
+ item.add(topologyNodePanel("conn", topologynode, syncopeTopologyNode, true));
+
+ if (conn.getLocation().startsWith(CONNECTOR_SERVER_LOCATION_PREFIX)) {
+ final Map<String, TopologyNode> remoteConnections;
+
+ if (connections.containsKey(conn.getLocation())) {
+ remoteConnections = connections.get(conn.getLocation());
+ } else {
+ remoteConnections = new HashMap<>();
+ connections.put(conn.getLocation(), remoteConnections);
+ }
+ remoteConnections.put(conn.getDisplayName(), topologynode);
+ } else {
+ syncopeConnections.put(conn.getDisplayName(), topologynode);
+ }
+ }
+ };
+
+ conn1.setOutputMarkupId(true);
+ add(conn1);
+ // -----------------------------------------
+
+ // -----------------------------------------
+ // Add Connector Intances (second level)
+ // -----------------------------------------
+ final ListView<ConnInstanceTO> conn2
+ = new ListView<ConnInstanceTO>("conn2", connModel.getObject().getRight()) {
+
+ private static final long serialVersionUID = 6978621871488360381L;
+
+ private final int size = connModel.getObject().getRight().size() + 1;
+
+ @Override
+ protected void populateItem(final ListItem<ConnInstanceTO> item) {
+ final ConnInstanceTO conn = item.getModelObject();
+
+ final TopologyNode parent = servers.get(conn.getLocation());
+
+ int kx = size >= 6 ? 800 : (130 * size);
+
+ int x = (int) Math.round((parent == null ? origX : parent.getX())
+ + kx * Math.cos(Math.PI + Math.PI * (item.getIndex() + 1) / size));
+ int y = (int) Math.round((parent == null ? origY : parent.getY())
+ + 100 * Math.sin(Math.PI + Math.PI * (item.getIndex() + 1) / size));
+
+ final TopologyNode topologynode = new TopologyNode(
+ Long.valueOf(conn.getKey()), conn.getDisplayName(), TopologyNode.Kind.CONNECTOR);
+ topologynode.setConnectinDisplayName(conn.getBundleName());
+ topologynode.setX(x);
+ topologynode.setY(y);
+
+ connectors.put(topologynode.getDisplayName(), topologynode);
+
+ item.add(topologyNodePanel("conn", topologynode, parent, true));
+
+ if (conn.getLocation().startsWith(CONNECTOR_SERVER_LOCATION_PREFIX)) {
+ final Map<String, TopologyNode> remoteConnections;
+
+ if (connections.containsKey(conn.getLocation())) {
+ remoteConnections = connections.get(conn.getLocation());
+ } else {
+ remoteConnections = new HashMap<>();
+ connections.put(conn.getLocation(), remoteConnections);
+ }
+ remoteConnections.put(conn.getDisplayName(), topologynode);
+ } else {
+ syncopeConnections.put(conn.getDisplayName(), topologynode);
+ }
+ }
+ };
+
+ conn2.setOutputMarkupId(true);
+ add(conn2);
+ // -----------------------------------------
+
+ // -----------------------------------------
+ // Add Resources
+ // -----------------------------------------
+ final List<String> connToBeProcessed = new ArrayList<>();
+ for (ResourceTO resourceTO : resModel.getObject()) {
+ final TopologyNode topologynode = new TopologyNode(
+ resourceTO.getKey(), resourceTO.getKey(), TopologyNode.Kind.RESOURCE);
+ topologynode.setX(origX);
+ topologynode.setY(origY);
+
+ final Map<String, TopologyNode> remoteConnections;
+
+ if (connections.containsKey(resourceTO.getConnectorDisplayName())) {
+ remoteConnections = connections.get(resourceTO.getConnectorDisplayName());
+ } else {
+ remoteConnections = new HashMap<>();
+ connections.put(resourceTO.getConnectorDisplayName(), remoteConnections);
+ }
+
+ remoteConnections.put(topologynode.getDisplayName(), topologynode);
+
+ if (!connToBeProcessed.contains(resourceTO.getConnectorDisplayName())) {
+ connToBeProcessed.add(resourceTO.getConnectorDisplayName());
+ }
+ }
+
+ final ListView<String> resources = new ListView<String>("resources", connToBeProcessed) {
+
+ private static final long serialVersionUID = 697862187148836038L;
+
+ @Override
+ protected void populateItem(final ListItem<String> item) {
+ final String connectorDisplayName = item.getModelObject();
+
+ final ListView<TopologyNode> innerListView = new ListView<TopologyNode>("resources",
+ new ArrayList<>(connections.get(connectorDisplayName).values())) {
+
+ private static final long serialVersionUID = 1L;
+
+ private final int size = getModelObject().size() + 1;
+
+ @Override
+ protected void populateItem(final ListItem<TopologyNode> item) {
+ final TopologyNode topologynode = item.getModelObject();
+ final TopologyNode parent = connectors.get(connectorDisplayName);
+
+ final double k;
+
+ if (parent == null || parent.getY() < syncopeTopologyNode.getY()) {
+ k = Math.PI;
+ } else {
+ k = 0.0;
+ }
+
+ int kx = size >= 16 ? 800 : (48 * size);
+ int ky = size < 4 ? 100 : size < 6 ? 350 : 750;
+
+ int x = (int) Math.round((parent == null ? origX : parent.getX())
+ + kx * Math.cos(k + Math.PI * (item.getIndex() + 1) / size));
+ int y = (int) Math.round((parent == null ? origY : parent.getY())
+ + ky * Math.sin(k + Math.PI * (item.getIndex() + 1) / size));
+
+ topologynode.setX(x);
+ topologynode.setY(y);
+
+ final Panel panel = new TopologyNodePanel(
+ "res", topologynode, getPageReference(), resourceRestClient);
+ panel.setMarkupId(topologynode.getDisplayName());
+ panel.setOutputMarkupId(true);
+ item.add(topologyNodePanel("res", topologynode, parent, true));
+ }
+ };
+
+ innerListView.setOutputMarkupId(true);
+ item.add(innerListView);
+ }
+ };
+
+ resources.setOutputMarkupId(true);
+ add(resources);
+ // -----------------------------------------
+
+ // -----------------------------------------
+ // Create connections
+ // -----------------------------------------
+ jsPlace.add(new Behavior() {
+
+ private static final long serialVersionUID = 2661717818979056044L;
+
+ @Override
+ public void renderHead(final Component component, final IHeaderResponse response) {
+ final StringBuilder jsPlumbConf = new StringBuilder();
+ jsPlumbConf.append(String.format(Locale.US, "activate(%.2f);", 0.68f));
+
+ for (String str : createConnections(connections)) {
+ jsPlumbConf.append(str);
+ }
+
+ response.render(OnDomReadyHeaderItem.forScript(jsPlumbConf.toString()));
+ }
+ });
+
+ jsPlace.setOutputMarkupId(true);
+ add(jsPlace);
+ // -----------------------------------------
+ }
+
+ @Override
+ public String getAjaxIndicatorMarkupId() {
+ return StringUtils.EMPTY;
+ }
+
+ private List<String> createConnections(final Map<String, Map<String, TopologyNode>> targets) {
+ List<String> list = new ArrayList<>();
+
+ for (Map.Entry<String, Map<String, TopologyNode>> source : targets.entrySet()) {
+ for (Map.Entry<String, TopologyNode> target : source.getValue().entrySet()) {
+ list.add(String.format("jsPlumb.connect({source:'%s',target:'%s'},def);",
+ source.getKey(),
+ target.getKey()));
+ }
+ }
+ return list;
+ }
+
+ private Panel topologyNodePanel(
+ final String id, final TopologyNode node, final TopologyNode parent, final boolean active) {
+ final Panel panel = new TopologyNodePanel(id, node, getPageReference(), resourceRestClient);
+ panel.setMarkupId(node.getDisplayName());
+ panel.setOutputMarkupId(true);
+
+ panel.add(new Behavior() {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public void renderHead(final Component component, final IHeaderResponse response) {
+ response.render(OnDomReadyHeaderItem.forScript(String.format("setPosition('%s', %d, %d)",
+ node.getDisplayName(), node.getX(), node.getY())));
+ }
+ });
+
+ final WebMarkupContainer timer = new WebMarkupContainer("timer");
+ timer.setOutputMarkupId(true);
+ panel.add(timer);
+
+ if (active) {
+ timer.add(new AbstractAjaxTimerBehavior(Duration.seconds(2)) {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void onTimer(final AjaxRequestTarget target) {
+ final List<TopologyReloadBehavior> behaviors = panel.getBehaviors(TopologyReloadBehavior.class);
+ for (TopologyReloadBehavior behavior : behaviors) {
+ panel.remove(behavior);
+ }
+
+ TopologyNode.Status status;
+
+ final FutureTask<TopologyNode.Status> future = new FutureTask<>(
+ new Callable<TopologyNode.Status>() {
+
+ @Override
+ public TopologyNode.Status call() throws Exception {
+ switch (node.getKind()) {
+ case CONNECTOR:
+ final ConnInstanceTO connector = connectorRestClient.
+ read(Long.class.cast(node.getKey()));
+ return connectorRestClient.check(connector) ? REACHABLE : UNREACHABLE;
+ case RESOURCE:
+ final ResourceTO resource = resourceRestClient.
+ read(String.class.cast(node.getKey()));
+ return connectorRestClient.check(resource) ? REACHABLE : UNREACHABLE;
+ default:
+ return UNKNOWN;
+ }
+ }
+ });
+
+ future.run();
+
+ try {
+ status = future.get(10, TimeUnit.SECONDS);
+ } catch (TimeoutException te) {
+ LOG.warn("Check connection timed out");
+ status = UNKNOWN;
+ } catch (InterruptedException | ExecutionException ie) {
+ // ignore
+ LOG.warn("Check connection failed", ie);
+ status = FAILURE;
+ }
+
+ timer.add(new TopologyReloadBehavior(parent.getDisplayName(), node.getDisplayName(), status));
+
+ if (getUpdateInterval().seconds() < 60.0) {
+ setUpdateInterval(Duration.seconds(60));
+ }
+
+ target.add(timer);
+ }
+ });
+ }
+
+ return panel;
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/34ec6712/client/console/src/main/java/org/apache/syncope/client/console/topology/TopologyNode.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/topology/TopologyNode.java b/client/console/src/main/java/org/apache/syncope/client/console/topology/TopologyNode.java
new file mode 100644
index 0000000..f7c8aaa
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/topology/TopologyNode.java
@@ -0,0 +1,123 @@
+/*
+ * 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.topology;
+
+import java.io.Serializable;
+
+public class TopologyNode implements Serializable {
+
+ public enum Kind {
+
+ RESOURCE,
+ CONNECTOR,
+ CONNECTOR_SERVER,
+ SYNCOPE
+
+ }
+
+ public enum Status {
+
+ UNKNOWN,
+ REACHABLE,
+ UNREACHABLE,
+ FAILURE
+
+ }
+
+ private static final long serialVersionUID = -1506421230369224142L;
+
+ private final Serializable key;
+
+ private final String displayName;
+
+ private String connectionDisplayName;
+
+ private Kind kind;
+
+ private String host;
+
+ private int port;
+
+ private int x;
+
+ private int y;
+
+ public TopologyNode(final Serializable key, final String displayName, final Kind kind) {
+ this.key = key;
+ this.displayName = displayName;
+ this.kind = kind;
+ }
+
+ public Serializable getKey() {
+ return key;
+ }
+
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ public String getConnectionDisplayName() {
+ return connectionDisplayName;
+ }
+
+ public void setConnectinDisplayName(final String connectinDisplayName) {
+ this.connectionDisplayName = connectinDisplayName;
+ }
+
+ public Kind getKind() {
+ return kind;
+ }
+
+ public void setKind(final Kind kind) {
+ this.kind = kind;
+ }
+
+ public int getX() {
+ return x;
+ }
+
+ public void setX(final int x) {
+ this.x = x;
+ }
+
+ public int getY() {
+ return y;
+ }
+
+ public void setY(final int y) {
+ this.y = y;
+ }
+
+ public String getHost() {
+ return host;
+ }
+
+ public void setHost(final String host) {
+ this.host = host;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public void setPort(final int port) {
+ this.port = port;
+ }
+
+}