http://git-wip-us.apache.org/repos/asf/syncope/blob/4e0783e1/client/console/src/main/java/org/apache/syncope/client/console/topology/Topology.java
----------------------------------------------------------------------
diff --cc 
client/console/src/main/java/org/apache/syncope/client/console/topology/Topology.java
index 409a16b,0000000..24cd09d
mode 100644,000000..100644
--- 
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
@@@ -1,570 -1,0 +1,570 @@@
 +/*
 + * 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;
 +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 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.panels.AbstractResourceModal.CreateEvent;
 +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.ajax.IAjaxIndicatorAware;
 +import org.apache.wicket.behavior.Behavior;
 +import org.apache.wicket.event.IEvent;
 +import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
 +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.model.LoadableDetachableModel;
 +import org.apache.wicket.util.time.Duration;
 +
 +public class Topology extends BasePage {
 +
 +    private static final long serialVersionUID = -1100228004207271272L;
 +
 +    public static final String CONNECTOR_SERVER_LOCATION_PREFIX = "connid://";
 +
 +    public static final String ROOT_NAME = "Syncope";
 +
 +    private final int origX = 3100;
 +
 +    private final int origY = 2800;
 +
 +    private static final int RESOURCE_MODAL_WIN_HEIGHT = 700;
 +
 +    private static final int RESOURCE_MODAL_WIN_WIDTH = 1000;
 +
 +    private final ModalWindow modal;
 +
 +    private final WebMarkupContainer newlyCreatedContainer;
 +
 +    private final ListView<TopologyNode> newlyCreated;
 +
 +    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<Map<String, List<ConnInstanceTO>>> 
connModel
 +            = new LoadableDetachableModel<Map<String, 
List<ConnInstanceTO>>>() {
 +
 +                private static final long serialVersionUID = 
5275935387613157432L;
 +
 +                @Override
 +                protected Map<String, List<ConnInstanceTO>> load() {
 +                    final Map<String, List<ConnInstanceTO>> res = new 
HashMap<>();
 +
 +                    for (ConnInstanceTO conn : 
connectorRestClient.getAllConnectors()) {
 +                        final List<ConnInstanceTO> conns;
 +                        if (res.containsKey(conn.getLocation())) {
 +                            conns = res.get(conn.getLocation());
 +                        } else {
 +                            conns = new ArrayList<>();
 +                            res.put(conn.getLocation(), conns);
 +                        }
 +                        conns.add(conn);
 +                    }
 +
 +                    return res;
 +                }
 +            };
 +
 +    private final LoadableDetachableModel<Pair<List<URI>, List<URI>>> csModel
 +            = new LoadableDetachableModel<Pair<List<URI>, List<URI>>>() {
 +
 +                private static final long serialVersionUID = 
5275935387613157433L;
 +
 +                @Override
 +                protected Pair<List<URI>, List<URI>> load() {
 +                    final List<URI> connectorServers = new ArrayList<>();
 +                    final List<URI> filePaths = new ArrayList<>();
 +
 +                    for (String location : 
SyncopeConsoleSession.get().getSyncopeTO().getConnIdLocations()) {
 +                        if 
(location.startsWith(CONNECTOR_SERVER_LOCATION_PREFIX)) {
 +                            connectorServers.add(URI.create(location));
 +                        } else {
 +                            filePaths.add(URI.create(location));
 +                        }
 +                    }
 +
 +                    return Pair.of(connectorServers, filePaths);
 +                }
 +            };
 +
 +    protected enum SupportedOperation {
 +
 +        CHECK_RESOURCE,
 +        CHECK_CONNECTOR,
 +        ADD_ENDPOINT;
 +
 +    }
 +
 +    public Topology() {
 +        modal = new ModalWindow("modal");
 +        add(modal);
 +
 +        modal.setCssClassName(ModalWindow.CSS_CLASS_GRAY);
 +        modal.setInitialHeight(RESOURCE_MODAL_WIN_HEIGHT);
 +        modal.setInitialWidth(RESOURCE_MODAL_WIN_WIDTH);
 +        modal.setCookieName("resource-modal");
 +
 +        add(new WebSocketBehavior());
 +
 +        // -----------------------------------------
 +        // Add Zoom panel
 +        // -----------------------------------------
 +        final ActionLinksPanel.Builder<Serializable> zoomActionPanel = 
ActionLinksPanel.builder(getPageReference());
 +        zoomActionPanel.setDisableIndicator(true);
 +
 +        zoomActionPanel.add(new ActionLink<Serializable>() {
 +
 +            private static final long serialVersionUID = 
-3722207913631435501L;
 +
 +            @Override
 +            public void onClick(final AjaxRequestTarget target, final 
Serializable ignore) {
 +                target.appendJavaScript("zoomIn($('#drawing')[0]);");
 +            }
 +        }, ActionLink.ActionType.ZOOM_IN, Entitlement.RESOURCE_LIST).add(new 
ActionLink<Serializable>() {
 +
 +            private static final long serialVersionUID = 
-3722207913631435501L;
 +
 +            @Override
 +            public void onClick(final AjaxRequestTarget target, final 
Serializable ignore) {
 +                target.appendJavaScript("zoomOut($('#drawing')[0]);");
 +            }
 +        }, ActionLink.ActionType.ZOOM_OUT, Entitlement.RESOURCE_LIST);
 +
 +        add(zoomActionPanel.build("zoom"));
 +        // -----------------------------------------
 +
 +        // -----------------------------------------
 +        // Add Syncope (root topologynode)
 +        // -----------------------------------------
 +        final TopologyNode syncopeTopologyNode = new TopologyNode(ROOT_NAME, 
ROOT_NAME, 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));
 +
 +        final Map<Serializable, Map<Serializable, TopologyNode>> connections 
= new HashMap<>();
 +        final Map<Serializable, TopologyNode> syncopeConnections = new 
HashMap<>();
 +        connections.put(syncopeTopologyNode.getKey(), syncopeConnections);
 +
 +        // required to retrieve parent positions
 +        final Map<String, TopologyNode> servers = new HashMap<>();
 +        final Map<Long, TopologyNode> connectors = new HashMap<>();
 +        // -----------------------------------------
 +
 +        // -----------------------------------------
 +        // Add Connector Servers
 +        // -----------------------------------------
 +        final ListView<URI> connectorServers = new 
ListView<URI>("connectorServers", csModel.getObject().getLeft()) {
 +
 +            private static final long serialVersionUID = 6978621871488360380L;
 +
 +            private final int size = csModel.getObject().getLeft().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(String.class.cast(topologynode.getKey()), 
topologynode);
 +
 +                item.add(topologyNodePanel("cs", topologynode));
 +
 +                syncopeConnections.put(url, topologynode);
 +                connections.put(url, new HashMap<Serializable, 
TopologyNode>());
 +            }
 +        };
 +
 +        connectorServers.setOutputMarkupId(true);
 +        add(connectorServers);
 +        // -----------------------------------------
 +
 +        // -----------------------------------------
 +        // Add File Paths
 +        // -----------------------------------------
 +        final ListView<URI> filePaths = new ListView<URI>("filePaths", 
csModel.getObject().getRight()) {
 +
 +            private static final long serialVersionUID = 6978621871488360380L;
 +
 +            private final int size = csModel.getObject().getRight().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 * 
(item.getIndex() + 1) / size));
 +                int y = (int) Math.round(origY + 100 * Math.sin(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.FS_PATH);
 +
 +                topologynode.setHost(location.getHost());
 +                topologynode.setPort(location.getPort());
 +                topologynode.setX(x);
 +                topologynode.setY(y);
 +
 +                servers.put(String.class.cast(topologynode.getKey()), 
topologynode);
 +
 +                item.add(topologyNodePanel("fp", topologynode));
 +
 +                syncopeConnections.put(url, topologynode);
 +                connections.put(url, new HashMap<Serializable, 
TopologyNode>());
 +            }
 +        };
 +
 +        filePaths.setOutputMarkupId(true);
 +        add(filePaths);
 +        // -----------------------------------------
 +
 +        // -----------------------------------------
 +        // Add Connector Intances
 +        // -----------------------------------------
 +        final List<List<ConnInstanceTO>> allConns = new 
ArrayList<>(connModel.getObject().values());
 +
 +        final ListView<List<ConnInstanceTO>> conns = new 
ListView<List<ConnInstanceTO>>("conns", allConns) {
 +
 +            private static final long serialVersionUID = 697862187148836036L;
 +
 +            @Override
 +            protected void populateItem(final ListItem<List<ConnInstanceTO>> 
item) {
 +
 +                final int size = item.getModelObject().size() + 1;
 +
 +                final ListView<ConnInstanceTO> conns = new 
ListView<ConnInstanceTO>("conns", item.getModelObject()) {
 +
 +                    private static final long serialVersionUID = 
6978621871488360381L;
 +
 +                    @Override
 +                    protected void populateItem(final 
ListItem<ConnInstanceTO> item) {
 +                        final ConnInstanceTO conn = item.getModelObject();
 +
 +                        final TopologyNode topologynode = new TopologyNode(
 +                                Long.valueOf(conn.getKey()), 
conn.getDisplayName(), TopologyNode.Kind.CONNECTOR);
 +
 +                        // Define the parent note
 +                        final TopologyNode parent = 
servers.get(conn.getLocation());
 +
 +                        // Set the position
 +                        int kx = size >= 6 ? 800 : (130 * size);
 +
 +                        final double hpos;
 +                        if 
(conn.getLocation().startsWith(CONNECTOR_SERVER_LOCATION_PREFIX)) {
 +                            hpos = Math.PI;
 +                        } else {
 +                            hpos = 0.0;
 +                        }
 +
 +                        int x = (int) Math.round((parent == null ? origX : 
parent.getX())
 +                                + kx * Math.cos(hpos + Math.PI * 
(item.getIndex() + 1) / size));
 +                        int y = (int) Math.round((parent == null ? origY : 
parent.getY())
 +                                + 100 * Math.sin(hpos + Math.PI * 
(item.getIndex() + 1) / size));
 +
 +                        
topologynode.setConnectionDisplayName(conn.getBundleName());
 +                        topologynode.setX(x);
 +                        topologynode.setY(y);
 +
 +                        
connectors.put(Long.class.cast(topologynode.getKey()), topologynode);
 +                        item.add(topologyNodePanel("conn", topologynode));
 +
 +                        // Update connections
 +                        final Map<Serializable, TopologyNode> 
remoteConnections;
 +
 +                        if (connections.containsKey(conn.getLocation())) {
 +                            remoteConnections = 
connections.get(conn.getLocation());
 +                        } else {
 +                            remoteConnections = new HashMap<>();
 +                            connections.put(conn.getLocation(), 
remoteConnections);
 +                        }
 +                        remoteConnections.put(conn.getKey(), topologynode);
 +                    }
 +                };
 +
 +                conns.setOutputMarkupId(true);
 +                item.add(conns);
 +            }
 +        };
 +
 +        conns.setOutputMarkupId(true);
 +        add(conns);
 +        // -----------------------------------------
 +
 +        // -----------------------------------------
 +        // Add Resources
 +        // -----------------------------------------
 +        final List<Long> connToBeProcessed = new ArrayList<>();
 +        for (ResourceTO resourceTO : resModel.getObject()) {
 +            final TopologyNode topologynode = new TopologyNode(
 +                    resourceTO.getKey(), resourceTO.getKey(), 
TopologyNode.Kind.RESOURCE);
 +
 +            final Map<Serializable, TopologyNode> remoteConnections;
 +
 +            if (connections.containsKey(resourceTO.getConnector())) {
 +                remoteConnections = 
connections.get(resourceTO.getConnector());
 +            } else {
 +                remoteConnections = new HashMap<>();
 +                connections.put(resourceTO.getConnector(), remoteConnections);
 +            }
 +
 +            remoteConnections.put(topologynode.getKey(), topologynode);
 +
 +            if (!connToBeProcessed.contains(resourceTO.getConnector())) {
 +                connToBeProcessed.add(resourceTO.getConnector());
 +            }
 +        }
 +
 +        final ListView<Long> resources = new ListView<Long>("resources", 
connToBeProcessed) {
 +
 +            private static final long serialVersionUID = 697862187148836038L;
 +
 +            @Override
 +            protected void populateItem(final ListItem<Long> item) {
 +                final Long connectorId = item.getModelObject();
 +
 +                final ListView<TopologyNode> innerListView = new 
ListView<TopologyNode>("resources",
 +                        new 
ArrayList<>(connections.get(connectorId).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(connectorId);
 +
 +                                // Set position
 +                                int kx = size >= 16 ? 800 : (48 * size);
 +                                int ky = size < 4 ? 100 : size < 6 ? 350 : 
750;
 +
 +                                final double hpos;
 +                                if (parent == null || parent.getY() < 
syncopeTopologyNode.getY()) {
 +                                    hpos = Math.PI;
 +                                } else {
 +                                    hpos = 0.0;
 +                                }
 +
 +                                int x = (int) Math.round((parent == null ? 
origX : parent.getX())
 +                                        + kx * Math.cos(hpos + Math.PI * 
(item.getIndex() + 1) / size));
 +                                int y = (int) Math.round((parent == null ? 
origY : parent.getY())
 +                                        + ky * Math.sin(hpos + Math.PI * 
(item.getIndex() + 1) / size));
 +
 +                                topologynode.setX(x);
 +                                topologynode.setY(y);
 +
 +                                item.add(topologyNodePanel("res", 
topologynode));
 +                            }
 +                        };
 +
 +                innerListView.setOutputMarkupId(true);
 +                item.add(innerListView);
 +            }
 +        };
 +
 +        resources.setOutputMarkupId(true);
 +        add(resources);
 +        // -----------------------------------------
 +
 +        // -----------------------------------------
 +        // Create connections
 +        // -----------------------------------------
 +        final WebMarkupContainer jsPlace = new 
WebMarkupContainerNoVeil("jsPlace");
 +        jsPlace.setOutputMarkupId(true);
 +        add(jsPlace);
 +
 +        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.add(new AbstractAjaxTimerBehavior(Duration.seconds(2)) {
 +
 +            private static final long serialVersionUID = 1L;
 +
 +            @Override
 +            protected void onTimer(final AjaxRequestTarget target) {
 +                target.appendJavaScript("checkConnection()");
 +
 +                if (getUpdateInterval().seconds() < 5.0) {
 +                    setUpdateInterval(Duration.seconds(5));
 +                } else if (getUpdateInterval().seconds() < 10.0) {
 +                    setUpdateInterval(Duration.seconds(10));
 +                } else if (getUpdateInterval().seconds() < 15.0) {
 +                    setUpdateInterval(Duration.seconds(15));
 +                } else if (getUpdateInterval().seconds() < 20.0) {
 +                    setUpdateInterval(Duration.seconds(20));
 +                } else if (getUpdateInterval().seconds() < 30.0) {
 +                    setUpdateInterval(Duration.seconds(30));
 +                } else if (getUpdateInterval().seconds() < 60.0) {
 +                    setUpdateInterval(Duration.seconds(60));
 +                }
 +            }
 +        });
 +        // -----------------------------------------
 +
 +        newlyCreatedContainer = new 
WebMarkupContainer("newlyCreatedContainer");
 +        newlyCreatedContainer.setOutputMarkupId(true);
 +        add(newlyCreatedContainer);
 +
 +        newlyCreated = new ListView<TopologyNode>("newlyCreated", new 
ArrayList<TopologyNode>()) {
 +
 +            private static final long serialVersionUID = 1L;
 +
 +            @Override
 +            protected void populateItem(final ListItem<TopologyNode> item) {
 +                item.add(topologyNodePanel("el", item.getModelObject()));
 +            }
 +        };
 +        newlyCreated.setOutputMarkupId(true);
 +        newlyCreated.setReuseItems(true);
 +
 +        newlyCreatedContainer.add(newlyCreated);
 +    }
 +
 +    private List<String> createConnections(final Map<Serializable, 
Map<Serializable, TopologyNode>> targets) {
 +        List<String> list = new ArrayList<>();
 +
 +        for (Map.Entry<Serializable, Map<Serializable, TopologyNode>> source 
: targets.entrySet()) {
 +            for (Map.Entry<Serializable, TopologyNode> target : 
source.getValue().entrySet()) {
 +                list.add(String.format("connect('%s','%s','%s');",
 +                        source.getKey(),
 +                        target.getKey(),
 +                        target.getValue().getKind()));
 +            }
 +        }
 +        return list;
 +    }
 +
 +    private TopologyNodePanel topologyNodePanel(final String id, final 
TopologyNode node) {
 +
 +        final TopologyNodePanel panel = new TopologyNodePanel(id, node, 
getPageReference(), modal);
 +        panel.setMarkupId(String.valueOf(node.getKey()));
 +        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.getKey(), node.getX(), node.getY())));
 +            }
 +        });
 +
 +        return panel;
 +    }
 +
 +    @Override
 +    @SuppressWarnings("unchecked")
 +    public void onEvent(final IEvent<?> event) {
 +        super.onEvent(event);
 +
 +        if (event.getPayload() instanceof CreateEvent) {
 +            final CreateEvent resourceCreateEvent = 
CreateEvent.class.cast(event.getPayload());
 +
 +            final TopologyNode node = new TopologyNode(
 +                    resourceCreateEvent.getKey(),
 +                    resourceCreateEvent.getDisplayName(),
 +                    resourceCreateEvent.getKind());
 +
 +            newlyCreated.getModelObject().add(node);
 +            resourceCreateEvent.getTarget().add(newlyCreatedContainer);
 +
 +            resourceCreateEvent.getTarget().appendJavaScript(String.format(
 +                    "window.Wicket.WebSocket.send('"
 +                    + 
"{\"kind\":\"%s\",\"target\":\"%s\",\"source\":\"%s\",\"scope\":\"%s\"}"
 +                    + "');",
 +                    SupportedOperation.ADD_ENDPOINT,
 +                    resourceCreateEvent.getKey(),
 +                    resourceCreateEvent.getParent(),
 +                    resourceCreateEvent.getKind()));
 +        }
 +    }
 +
 +    private static class WebMarkupContainerNoVeil extends WebMarkupContainer 
implements IAjaxIndicatorAware {
 +
 +        private static final long serialVersionUID = 1L;
 +
-         public WebMarkupContainerNoVeil(final String id) {
++        WebMarkupContainerNoVeil(final String id) {
 +            super(id);
 +        }
 +
 +        @Override
 +        public String getAjaxIndicatorMarkupId() {
 +            return StringUtils.EMPTY;
 +        }
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/4e0783e1/client/console/src/main/java/org/apache/syncope/client/console/topology/WebSocketBehavior.java
----------------------------------------------------------------------
diff --cc 
client/console/src/main/java/org/apache/syncope/client/console/topology/WebSocketBehavior.java
index 1b847f9,0000000..8e3267d
mode 100644,000000..100644
--- 
a/client/console/src/main/java/org/apache/syncope/client/console/topology/WebSocketBehavior.java
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/topology/WebSocketBehavior.java
@@@ -1,208 -1,0 +1,208 @@@
 +/*
 + * 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 com.fasterxml.jackson.databind.JsonNode;
 +import com.fasterxml.jackson.databind.ObjectMapper;
 +import java.io.IOException;
 +import java.util.HashMap;
 +import java.util.HashSet;
 +import java.util.Map;
 +import java.util.Set;
 +import java.util.concurrent.ExecutorService;
 +import java.util.concurrent.Executors;
 +import org.apache.syncope.client.console.rest.ConnectorRestClient;
 +import org.apache.syncope.client.console.rest.ResourceRestClient;
 +import org.apache.syncope.common.lib.to.ConnInstanceTO;
 +import org.apache.syncope.common.lib.to.ResourceTO;
 +import org.apache.wicket.Application;
 +import org.apache.wicket.Session;
 +import org.apache.wicket.ThreadContext;
 +import org.apache.wicket.protocol.ws.api.WebSocketRequestHandler;
 +import org.apache.wicket.protocol.ws.api.message.TextMessage;
 +import org.slf4j.Logger;
 +import org.slf4j.LoggerFactory;
 +
 +public class WebSocketBehavior extends 
org.apache.wicket.protocol.ws.api.WebSocketBehavior {
 +
 +    protected static final Logger LOG = 
LoggerFactory.getLogger(WebSocketBehavior.class);
 +
 +    private static final long serialVersionUID = -1653665542635275551L;
 +
 +    private final Map<String, String> resources = new HashMap<>();
 +
 +    private final Set<String> runningResCheck = new HashSet<>();
 +
 +    private final Map<Long, String> connectors = new HashMap<>();
 +
 +    private final Set<Long> runningConnCheck = new HashSet<>();
 +
 +    private final ConnectorRestClient connectorRestClient = new 
ConnectorRestClient();
 +
 +    private final ResourceRestClient resourceRestClient = new 
ResourceRestClient();
 +
 +    @Override
 +    protected void onMessage(final WebSocketRequestHandler handler, final 
TextMessage message) {
 +        try {
 +            final ObjectMapper mapper = new ObjectMapper();
 +            final JsonNode obj = mapper.readTree(message.getText());
 +
 +            final ExecutorService executorService = 
Executors.newFixedThreadPool(1);
 +
 +            switch 
(Topology.SupportedOperation.valueOf(obj.get("kind").asText())) {
 +                case CHECK_CONNECTOR:
 +                    final Long ckey = obj.get("target").asLong();
 +
 +                    if (connectors.containsKey(ckey)) {
 +                        handler.push(connectors.get(ckey));
 +                    } else {
 +                        handler.push(String.format(
 +                                "{ \"status\": \"%s\", \"target\": \"%s\"}", 
TopologyNode.Status.UNKNOWN, ckey));
 +                    }
 +
 +                    synchronized (runningConnCheck) {
 +                        if (runningConnCheck.contains(ckey)) {
 +                            LOG.debug("Running connection check for connector 
{}", ckey);
 +                        } else {
 +                            runningConnCheck.add(ckey);
 +                        }
 +                    }
 +
 +                    executorService.execute(new ConnCheck(ckey));
 +
 +                    break;
 +                case CHECK_RESOURCE:
 +                    final String rkey = obj.get("target").asText();
 +
 +                    if (resources.containsKey(rkey)) {
 +                        handler.push(resources.get(rkey));
 +                    } else {
 +                        handler.push(String.format(
 +                                "{ \"status\": \"%s\", \"target\": \"%s\"}", 
TopologyNode.Status.UNKNOWN, rkey));
 +                    }
 +
 +                    synchronized (runningResCheck) {
 +                        if (runningResCheck.contains(rkey)) {
 +                            LOG.debug("Running connection check for resource 
{}", rkey);
 +                        } else {
 +                            runningResCheck.add(rkey);
 +                        }
 +                    }
 +
 +                    executorService.execute(new ResCheck(rkey));
 +
 +                    break;
 +                case ADD_ENDPOINT:
 +                    handler.appendJavaScript(String.format("addEndpoint('%s', 
'%s', '%s');",
 +                            obj.get("source").asText(),
 +                            obj.get("target").asText(),
 +                            obj.get("scope").asText()));
 +                    break;
 +                default:
 +            }
 +
 +            executorService.shutdown();
 +
 +        } catch (IOException e) {
 +            LOG.error("Eror managing websocket message", e);
 +        }
 +    }
 +
 +    class ConnCheck implements Runnable {
 +
 +        private final Long key;
 +
 +        private final Application application;
 +
 +        private final Session session;
 +
-         public ConnCheck(final Long key) {
++        ConnCheck(final Long key) {
 +            this.key = key;
 +            this.application = Application.get();
 +            this.session = Session.exists() ? Session.get() : null;
 +        }
 +
 +        @Override
 +        public void run() {
 +            try {
 +                ThreadContext.setApplication(application);
 +                ThreadContext.setSession(session);
 +
 +                String res;
 +                try {
 +                    final ConnInstanceTO connector = 
connectorRestClient.read(key);
 +                    res = String.format("{ \"status\": \"%s\", \"target\": 
\"%s\"}",
 +                            connectorRestClient.check(connector)
 +                                    ? TopologyNode.Status.REACHABLE : 
TopologyNode.Status.UNREACHABLE, key);
 +                } catch (Exception e) {
 +                    LOG.warn("Error checking connection for {}", key, e);
 +                    res = String.format("{ \"status\": \"%s\", \"target\": 
\"%s\"}", TopologyNode.Status.FAILURE, key);
 +                }
 +
 +                synchronized (runningConnCheck) {
 +                    connectors.put(key, res);
 +                    runningConnCheck.remove(key);
 +                }
 +            } finally {
 +                ThreadContext.detach();
 +            }
 +        }
 +    }
 +
 +    class ResCheck implements Runnable {
 +
 +        private final String key;
 +
 +        private final Application application;
 +
 +        private final Session session;
 +
-         public ResCheck(final String key) {
++        ResCheck(final String key) {
 +            this.key = key;
 +            this.application = Application.get();
 +            this.session = Session.exists() ? Session.get() : null;
 +        }
 +
 +        @Override
 +        public void run() {
 +            try {
 +                ThreadContext.setApplication(application);
 +                ThreadContext.setSession(session);
 +
 +                String res;
 +                try {
 +                    final ResourceTO resource = resourceRestClient.read(key);
 +                    res = String.format("{ \"status\": \"%s\", \"target\": 
\"%s\"}",
 +                            connectorRestClient.check(resource)
 +                                    ? TopologyNode.Status.REACHABLE : 
TopologyNode.Status.UNREACHABLE, key);
 +                } catch (Exception e) {
 +                    LOG.warn("Error checking connection for {}", key, e);
 +                    res = String.format("{ \"status\": \"%s\", \"target\": 
\"%s\"}", TopologyNode.Status.FAILURE, key);
 +                }
 +
 +                synchronized (runningResCheck) {
 +                    resources.put(key, res);
 +                    runningResCheck.remove(key);
 +                }
 +            } finally {
 +                ThreadContext.detach();
 +            }
 +        }
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/4e0783e1/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLink.java
----------------------------------------------------------------------
diff --cc 
client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLink.java
index ec405a2,0000000..1c6731f
mode 100644,000000..100644
--- 
a/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLink.java
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLink.java
@@@ -1,102 -1,0 +1,102 @@@
 +/*
 + * 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.wicket.markup.html.form;
 +
 +import java.io.Serializable;
 +import org.apache.wicket.ajax.AjaxRequestTarget;
 +
 +public abstract class ActionLink<T> implements Serializable {
 +
 +    private static final long serialVersionUID = 7031329706998320639L;
 +
 +    private boolean reloadFeedbackPanel = true;
 +
 +    private T modelObject;
 +
 +    public ActionLink() {
 +    }
 +
 +    public ActionLink(final T modelObject) {
 +        this.modelObject = modelObject;
 +    }
 +
 +    public enum ActionType {
 +
 +        MAPPING("update"),
 +        ACCOUNT_LINK("update"),
 +        RESET_TIME("update"),
 +        CLONE("create"),
 +        CREATE("create"),
 +        EDIT("read"),
 +        USER_TEMPLATE("read"),
 +        GROUP_TEMPLATE("read"),
 +        RESET("update"),
 +        ENABLE("update"),
 +        SEARCH("read"),
 +        DELETE("delete"),
 +        EXECUTE("execute"),
 +        DRYRUN("execute"),
 +        CLAIM("claim"),
 +        SELECT("read"),
 +        EXPORT("read"),
 +        SUSPEND("update"),
 +        REACTIVATE("update"),
 +        RELOAD("reload"),
 +        CHANGE_VIEW("changeView"),
 +        UNLINK("update"),
 +        LINK("update"),
 +        UNASSIGN("update"),
 +        ASSIGN("update"),
 +        DEPROVISION("update"),
 +        PROVISION("update"),
 +        MANAGE_RESOURCES("update"),
 +        MANAGE_USERS("update"),
 +        MANAGE_GROUPS("update"),
 +        ZOOM_IN("zoomin"),
 +        ZOOM_OUT("zoomout");
 +
 +        private final String actionId;
 +
-         private ActionType(final String actionId) {
++        ActionType(final String actionId) {
 +            this.actionId = actionId;
 +        }
 +
 +        public String getActionId() {
 +            return actionId;
 +        }
 +    }
 +
 +    public T getModelObject() {
 +        return modelObject;
 +    }
 +
 +    public abstract void onClick(final AjaxRequestTarget target, final T 
modelObject);
 +
 +    public void postClick() {
 +    }
 +
 +    public boolean feedbackPanelAutomaticReload() {
 +        return reloadFeedbackPanel;
 +    }
 +
 +    public ActionLink<T> feedbackPanelAutomaticReload(final boolean 
reloadFeedbackPanel) {
 +        this.reloadFeedbackPanel = reloadFeedbackPanel;
 +        return this;
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/4e0783e1/client/console/src/main/java/org/apache/syncope/client/console/wizards/provision/ProvisionWizardBuilder.java
----------------------------------------------------------------------
diff --cc 
client/console/src/main/java/org/apache/syncope/client/console/wizards/provision/ProvisionWizardBuilder.java
index 30a7bbc,0000000..161df03
mode 100644,000000..100644
--- 
a/client/console/src/main/java/org/apache/syncope/client/console/wizards/provision/ProvisionWizardBuilder.java
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/wizards/provision/ProvisionWizardBuilder.java
@@@ -1,222 -1,0 +1,222 @@@
 +/*
 + * Copyright 2015 The Apache Software Foundation.
 + *
 + * 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.
 + */
 +package org.apache.syncope.client.console.wizards.provision;
 +
 +import java.io.Serializable;
 +import java.util.ArrayList;
 +import java.util.List;
 +import org.apache.commons.collections4.CollectionUtils;
 +import org.apache.commons.collections4.Predicate;
 +import org.apache.commons.collections4.Transformer;
 +import org.apache.commons.lang3.StringUtils;
 +import org.apache.syncope.client.console.commons.Constants;
 +import org.apache.syncope.client.console.panels.ResourceMappingPanel;
 +import org.apache.syncope.client.console.rest.AnyTypeRestClient;
 +import 
org.apache.syncope.client.console.wicket.markup.html.form.AjaxCheckBoxPanel;
 +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.wizards.AjaxWizardBuilder;
 +import org.apache.syncope.common.lib.to.AnyTypeTO;
 +import org.apache.syncope.common.lib.to.ProvisionTO;
 +import org.apache.syncope.common.lib.to.ResourceTO;
 +import org.apache.wicket.PageReference;
 +import org.apache.wicket.ajax.AjaxRequestTarget;
 +import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
 +import org.apache.wicket.extensions.wizard.WizardModel;
 +import org.apache.wicket.extensions.wizard.WizardStep;
 +import org.apache.wicket.markup.html.WebMarkupContainer;
 +import org.apache.wicket.markup.html.form.TextField;
 +import org.apache.wicket.model.LoadableDetachableModel;
 +import org.apache.wicket.model.Model;
 +import org.apache.wicket.model.PropertyModel;
 +import org.apache.wicket.model.ResourceModel;
 +import org.apache.wicket.model.StringResourceModel;
 +
 +public class ProvisionWizardBuilder extends AjaxWizardBuilder<ProvisionTO> 
implements Serializable {
 +
 +    private static final long serialVersionUID = 1L;
 +
 +    private final ResourceTO resourceTO;
 +
 +    private final LoadableDetachableModel<List<String>> anyTypes = new 
LoadableDetachableModel<List<String>>() {
 +
 +        private static final long serialVersionUID = 1L;
 +
 +        @Override
 +        protected List<String> load() {
 +            final List<String> currentlyAdded = new ArrayList<>();
 +
 +            CollectionUtils.collect(resourceTO.getProvisions(), new 
Transformer<ProvisionTO, String>() {
 +
 +                @Override
 +                public String transform(final ProvisionTO provisionTO) {
 +                    return provisionTO.getAnyType();
 +                }
 +            }, currentlyAdded);
 +
 +            final List<String> res = new ArrayList<>();
 +
 +            CollectionUtils.filter(
 +                    CollectionUtils.collect(new AnyTypeRestClient().getAll(), 
new Transformer<AnyTypeTO, String>() {
 +
 +                        @Override
 +                        public String transform(final AnyTypeTO anyTypeTO) {
 +                            return anyTypeTO.getKey();
 +                        }
 +                    }, res), new Predicate<String>() {
 +
 +                        @Override
 +                        public boolean evaluate(final String key) {
 +                            return !currentlyAdded.contains(key);
 +                        }
 +                    });
 +
 +            return res;
 +        }
 +    };
 +
 +    /**
 +     * The object type specification step.
 +     */
 +    private final class ObjectType extends WizardStep {
 +
 +        private static final long serialVersionUID = 1L;
 +
 +        /**
 +         * Construct.
 +         */
-         public ObjectType(final ProvisionTO item) {
++        ObjectType(final ProvisionTO item) {
 +            super(new ResourceModel("type.title", StringUtils.EMPTY),
 +                    new ResourceModel("type.summary", StringUtils.EMPTY), new 
Model<ProvisionTO>(item));
 +
 +            add(new AjaxDropDownChoicePanel<String>("type", "type", new 
PropertyModel<String>(item, "anyType"), false).
 +                    setChoices(anyTypes).
 +                    setStyleSheet("form-control").
 +                    setRequired(true));
 +
 +            add(new TextField<String>(
 +                    "class", new PropertyModel<String>(item, 
"objectClass")).setRequired(true));
 +        }
 +    }
 +
 +    /**
 +     * Mapping definition step.
 +     */
 +    private final class Mapping extends WizardStep {
 +
 +        private static final long serialVersionUID = 1L;
 +
 +        /**
 +         * Construct.
 +         */
-         public Mapping(final ProvisionTO item) {
++        Mapping(final ProvisionTO item) {
 +            setTitleModel(new ResourceModel("mapping.title", "Mapping"));
 +            setSummaryModel(new StringResourceModel("mapping.summary", this, 
new Model<ProvisionTO>(item)));
 +
 +            add(new ResourceMappingPanel("mapping", resourceTO, item));
 +        }
 +    }
 +
 +    /**
 +     * AccountLink specification step.
 +     */
 +    private final class ConnObjectLink extends WizardStep {
 +
 +        private static final long serialVersionUID = 1L;
 +
 +        /**
 +         * Construct.
 +         */
-         public ConnObjectLink(final ProvisionTO item) {
++        ConnObjectLink(final ProvisionTO item) {
 +            super(new ResourceModel("link.title", StringUtils.EMPTY),
 +                    new ResourceModel("link.summary", StringUtils.EMPTY));
 +
 +            final WebMarkupContainer connObjectLinkContainer = new 
WebMarkupContainer("connObjectLinkContainer");
 +            connObjectLinkContainer.setOutputMarkupId(true);
 +            add(connObjectLinkContainer);
 +
 +            boolean connObjectLinkEnabled = false;
 +            if 
(StringUtils.isNotBlank(item.getMapping().getConnObjectLink())) {
 +                connObjectLinkEnabled = true;
 +            }
 +
 +            final AjaxCheckBoxPanel connObjectLinkCheckbox = new 
AjaxCheckBoxPanel(
 +                    "connObjectLinkCheckbox",
 +                    new ResourceModel("connObjectLinkCheckbox", 
"connObjectLinkCheckbox").getObject(),
 +                    new Model<>(connObjectLinkEnabled),
 +                    false);
 +            connObjectLinkCheckbox.setEnabled(true);
 +
 +            connObjectLinkContainer.add(connObjectLinkCheckbox);
 +
 +            final AjaxTextFieldPanel connObjectLink = new AjaxTextFieldPanel(
 +                    "connObjectLink",
 +                    new ResourceModel("connObjectLink", 
"connObjectLink").getObject(),
 +                    new PropertyModel<String>(item.getMapping(), 
"connObjectLink"),
 +                    false);
 +            connObjectLink.setEnabled(connObjectLinkEnabled);
 +            connObjectLinkContainer.add(connObjectLink);
 +
 +            connObjectLinkCheckbox.getField().add(new 
AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
 +
 +                private static final long serialVersionUID = 
-1107858522700306810L;
 +
 +                @Override
 +                protected void onUpdate(final AjaxRequestTarget target) {
 +                    if (connObjectLinkCheckbox.getModelObject()) {
 +                        connObjectLink.setEnabled(Boolean.TRUE);
 +                        connObjectLink.setModelObject("");
 +                    } else {
 +                        connObjectLink.setEnabled(Boolean.FALSE);
 +                        connObjectLink.setModelObject("");
 +                    }
 +
 +                    target.add(connObjectLink);
 +                }
 +            });
 +        }
 +    }
 +
 +    /**
 +     * Construct.
 +     *
 +     * @param id The component id
 +     * @param resourceTO external resource to be updated.
 +     * @param pageRef Caller page reference.
 +     */
 +    public ProvisionWizardBuilder(final String id, final ResourceTO 
resourceTO, final PageReference pageRef) {
 +        super(id, new ProvisionTO(), pageRef);
 +        this.resourceTO = resourceTO;
 +    }
 +
 +    @Override
 +    protected WizardModel buildModelSteps(final ProvisionTO modelObject, 
final WizardModel wizardModel) {
 +        wizardModel.add(new ObjectType(modelObject));
 +        wizardModel.add(new Mapping(modelObject));
 +        wizardModel.add(new ConnObjectLink(modelObject));
 +        return wizardModel;
 +    }
 +
 +    @Override
 +    protected void onCancelInternal() {
 +        // d nothing
 +    }
 +
 +    @Override
 +    protected void onApplyInternal() {
 +        // do nothing
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/4e0783e1/client/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClientFactoryBean.java
----------------------------------------------------------------------
diff --cc 
client/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClientFactoryBean.java
index 46dc302,0000000..724da5a
mode 100644,000000..100644
--- 
a/client/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClientFactoryBean.java
+++ 
b/client/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClientFactoryBean.java
@@@ -1,254 -1,0 +1,254 @@@
 +/*
 + * 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.lib;
 +
 +import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
 +import java.util.ArrayList;
 +import java.util.Collections;
 +import java.util.HashMap;
 +import java.util.List;
 +import java.util.Map;
 +import javax.ws.rs.core.MediaType;
 +import javax.xml.bind.Marshaller;
 +import org.apache.commons.lang3.StringUtils;
 +import org.apache.cxf.feature.Feature;
 +import org.apache.cxf.feature.LoggingFeature;
 +import org.apache.cxf.jaxrs.provider.JAXBElementProvider;
 +import org.apache.cxf.staxutils.DocumentDepthProperties;
 +import org.apache.syncope.common.lib.policy.AbstractPolicyTO;
 +import org.apache.syncope.common.rest.api.RESTHeaders;
 +
 +/**
 + * Factory bean for creating instances of {@link SyncopeClient}.
 + * Supports Spring-bean configuration and override via subclassing (see 
protected methods).
 + */
 +public class SyncopeClientFactoryBean {
 +
 +    public enum ContentType {
 +
 +        JSON(MediaType.APPLICATION_JSON_TYPE),
 +        XML(MediaType.APPLICATION_XML_TYPE);
 +
 +        private final MediaType mediaType;
 +
-         private ContentType(final MediaType mediaType) {
++        ContentType(final MediaType mediaType) {
 +            this.mediaType = mediaType;
 +        }
 +
 +        public MediaType getMediaType() {
 +            return mediaType;
 +        }
 +
 +        public static ContentType fromString(final String value) {
 +            return StringUtils.isNotBlank(value) && 
value.equalsIgnoreCase(XML.getMediaType().toString())
 +                    ? XML
 +                    : JSON;
 +        }
 +    }
 +
 +    private JacksonJaxbJsonProvider jsonProvider;
 +
 +    private JAXBElementProvider<?> jaxbProvider;
 +
 +    private RestClientExceptionMapper exceptionMapper;
 +
 +    private String address;
 +
 +    private ContentType contentType;
 +
 +    private String domain;
 +
 +    private boolean useCompression;
 +
 +    private RestClientFactoryBean restClientFactoryBean;
 +
 +    protected JacksonJaxbJsonProvider defaultJsonProvider() {
 +        return new JacksonJaxbJsonProvider();
 +    }
 +
 +    @SuppressWarnings({ "rawtypes" })
 +    protected JAXBElementProvider<?> defaultJAXBProvider() {
 +        JAXBElementProvider<?> defaultJAXBProvider = new 
JAXBElementProvider();
 +
 +        DocumentDepthProperties depthProperties = new 
DocumentDepthProperties();
 +        depthProperties.setInnerElementCountThreshold(500);
 +        defaultJAXBProvider.setDepthProperties(depthProperties);
 +
 +        Map<String, Object> marshallerProperties = new HashMap<>();
 +        marshallerProperties.put(Marshaller.JAXB_FORMATTED_OUTPUT, 
Boolean.TRUE);
 +        defaultJAXBProvider.setMarshallerProperties(marshallerProperties);
 +
 +        Map<String, String> collectionWrapperMap = new HashMap<>();
 +        collectionWrapperMap.put(AbstractPolicyTO.class.getName(), 
"policies");
 +        defaultJAXBProvider.setCollectionWrapperMap(collectionWrapperMap);
 +
 +        return defaultJAXBProvider;
 +    }
 +
 +    protected RestClientExceptionMapper defaultExceptionMapper() {
 +        return new RestClientExceptionMapper();
 +    }
 +
 +    protected RestClientFactoryBean defaultRestClientFactoryBean() {
 +        RestClientFactoryBean defaultRestClientFactoryBean = new 
RestClientFactoryBean();
 +
 +        if (StringUtils.isBlank(address)) {
 +            throw new IllegalArgumentException("Property 'address' is 
missing");
 +        }
 +        defaultRestClientFactoryBean.setAddress(address);
 +
 +        if (StringUtils.isNotBlank(domain)) {
 +            
defaultRestClientFactoryBean.setHeaders(Collections.singletonMap(RESTHeaders.DOMAIN,
 domain));
 +        }
 +
 +        defaultRestClientFactoryBean.setThreadSafe(true);
 +        defaultRestClientFactoryBean.setInheritHeaders(true);
 +
 +        List<Feature> features = new ArrayList<>();
 +        features.add(new LoggingFeature());
 +        defaultRestClientFactoryBean.setFeatures(features);
 +
 +        List<Object> providers = new ArrayList<>(3);
 +        providers.add(getJaxbProvider());
 +        providers.add(getJsonProvider());
 +        providers.add(getExceptionMapper());
 +        defaultRestClientFactoryBean.setProviders(providers);
 +
 +        return defaultRestClientFactoryBean;
 +    }
 +
 +    public JacksonJaxbJsonProvider getJsonProvider() {
 +        return jsonProvider == null
 +                ? defaultJsonProvider()
 +                : jsonProvider;
 +    }
 +
 +    public void setJsonProvider(final JacksonJaxbJsonProvider jsonProvider) {
 +        this.jsonProvider = jsonProvider;
 +    }
 +
 +    public JAXBElementProvider<?> getJaxbProvider() {
 +        return jaxbProvider == null
 +                ? defaultJAXBProvider()
 +                : jaxbProvider;
 +    }
 +
 +    public SyncopeClientFactoryBean setJaxbProvider(final 
JAXBElementProvider<?> jaxbProvider) {
 +        this.jaxbProvider = jaxbProvider;
 +        return this;
 +    }
 +
 +    public RestClientExceptionMapper getExceptionMapper() {
 +        return exceptionMapper == null
 +                ? defaultExceptionMapper()
 +                : exceptionMapper;
 +    }
 +
 +    public SyncopeClientFactoryBean setExceptionMapper(final 
RestClientExceptionMapper exceptionMapper) {
 +        this.exceptionMapper = exceptionMapper;
 +        return this;
 +    }
 +
 +    public String getAddress() {
 +        return address;
 +    }
 +
 +    public SyncopeClientFactoryBean setAddress(final String address) {
 +        this.address = address;
 +        return this;
 +    }
 +
 +    public ContentType getContentType() {
 +        return contentType == null
 +                ? ContentType.JSON
 +                : contentType;
 +    }
 +
 +    public SyncopeClientFactoryBean setContentType(final ContentType 
contentType) {
 +        this.contentType = contentType;
 +        return this;
 +    }
 +
 +    public SyncopeClientFactoryBean setContentType(final String contentType) {
 +        this.contentType = ContentType.fromString(contentType);
 +        return this;
 +    }
 +
 +    public String getDomain() {
 +        return domain;
 +    }
 +
 +    public SyncopeClientFactoryBean setDomain(final String domain) {
 +        this.domain = domain;
 +        return this;
 +    }
 +
 +    /**
 +     * Sets the given service instance for transparent gzip 
<tt>Content-Encoding</tt> handling.
 +     *
 +     * @param useCompression whether transparent gzip 
<tt>Content-Encoding</tt> handling is to be enabled
 +     * @return the current instance
 +     */
 +    public SyncopeClientFactoryBean setUseCompression(final boolean 
useCompression) {
 +        this.useCompression = useCompression;
 +        return this;
 +    }
 +
 +    public boolean isUseCompression() {
 +        return useCompression;
 +    }
 +
 +    public RestClientFactoryBean getRestClientFactoryBean() {
 +        return restClientFactoryBean == null
 +                ? defaultRestClientFactoryBean()
 +                : restClientFactoryBean;
 +    }
 +
 +    public SyncopeClientFactoryBean setRestClientFactoryBean(final 
RestClientFactoryBean restClientFactoryBean) {
 +        this.restClientFactoryBean = restClientFactoryBean;
 +        return this;
 +    }
 +
 +    /**
 +     * Builds client instance with no authentication, for user 
self-registration and related queries (schema,
 +     * resources, ...).
 +     *
 +     * @return client instance with no authentication
 +     */
 +    public SyncopeClient create() {
 +        return create(null, null);
 +    }
 +
 +    /**
 +     * Builds client instance with the given credentials.
 +     *
 +     * @param username username
 +     * @param password password
 +     * @return client instance with the given credentials
 +     */
 +    public SyncopeClient create(final String username, final String password) 
{
 +        return new SyncopeClient(
 +                getContentType().getMediaType(),
 +                getRestClientFactoryBean(),
 +                getExceptionMapper(),
 +                username,
 +                password,
 +                useCompression);
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/4e0783e1/common/lib/src/main/java/org/apache/syncope/common/lib/types/AnyTypeKind.java
----------------------------------------------------------------------
diff --cc 
common/lib/src/main/java/org/apache/syncope/common/lib/types/AnyTypeKind.java
index ee35e28,0000000..136d798
mode 100644,000000..100644
--- 
a/common/lib/src/main/java/org/apache/syncope/common/lib/types/AnyTypeKind.java
+++ 
b/common/lib/src/main/java/org/apache/syncope/common/lib/types/AnyTypeKind.java
@@@ -1,44 -1,0 +1,44 @@@
 +/*
 + * 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.common.lib.types;
 +
 +import javax.xml.bind.annotation.XmlEnum;
 +import org.apache.syncope.common.lib.to.AnyObjectTO;
 +import org.apache.syncope.common.lib.to.AnyTO;
 +import org.apache.syncope.common.lib.to.GroupTO;
 +import org.apache.syncope.common.lib.to.UserTO;
 +
 +@XmlEnum
 +public enum AnyTypeKind {
 +
 +    USER(UserTO.class),
 +    GROUP(GroupTO.class),
 +    ANY_OBJECT(AnyObjectTO.class);
 +
 +    private final Class<? extends AnyTO> toClass;
 +
-     private AnyTypeKind(final Class<? extends AnyTO> toClass) {
++    AnyTypeKind(final Class<? extends AnyTO> toClass) {
 +        this.toClass = toClass;
 +    }
 +
 +    public Class<? extends AnyTO> getToClass() {
 +        return toClass;
 +    }
 +
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/4e0783e1/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java
----------------------------------------------------------------------
diff --cc 
common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java
index 0c19403,0000000..e4a0f10
mode 100644,000000..100644
--- 
a/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java
+++ 
b/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java
@@@ -1,103 -1,0 +1,103 @@@
 +/*
 + * 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.common.lib.types;
 +
 +import javax.ws.rs.core.Response;
 +
 +public enum ClientExceptionType {
 +
 +    AssociatedResources(Response.Status.BAD_REQUEST),
 +    Composite(Response.Status.BAD_REQUEST),
 +    ConcurrentModification(Response.Status.PRECONDITION_FAILED),
 +    ConnectorException(Response.Status.BAD_REQUEST),
 +    DataIntegrityViolation(Response.Status.CONFLICT),
 +    EntityExists(Response.Status.CONFLICT),
 +    GenericPersistence(Response.Status.BAD_REQUEST),
 +    InvalidSecurityAnswer(Response.Status.BAD_REQUEST),
 +    InvalidEntity(Response.Status.BAD_REQUEST),
 +    InvalidLogger(Response.Status.BAD_REQUEST),
 +    InvalidConnInstance(Response.Status.BAD_REQUEST),
 +    InvalidConnIdConf(Response.Status.BAD_REQUEST),
 +    InvalidPolicy(Response.Status.BAD_REQUEST),
 +    InvalidConf(Response.Status.BAD_REQUEST),
 +    InvalidPath(Response.Status.BAD_REQUEST),
 +    InvalidProvision(Response.Status.BAD_REQUEST),
 +    InvalidReport(Response.Status.BAD_REQUEST),
 +    InvalidReportExec(Response.Status.BAD_REQUEST),
 +    InvalidRelationshipType(Response.Status.BAD_REQUEST),
 +    InvalidAnyType(Response.Status.BAD_REQUEST),
 +    InvalidAnyObject(Response.Status.BAD_REQUEST),
 +    InvalidGroup(Response.Status.BAD_REQUEST),
 +    InvalidSchemaDefinition(Response.Status.BAD_REQUEST),
 +    InvalidSearchExpression(Response.Status.BAD_REQUEST),
 +    InvalidPageOrSize(Response.Status.BAD_REQUEST),
 +    InvalidPropagationTaskExecReport(Response.Status.BAD_REQUEST),
 +    InvalidPlainSchema(Response.Status.BAD_REQUEST),
 +    InvalidDerSchema(Response.Status.BAD_REQUEST),
 +    InvalidVirSchema(Response.Status.BAD_REQUEST),
 +    InvalidMapping(Response.Status.BAD_REQUEST),
 +    InvalidRealm(Response.Status.BAD_REQUEST),
 +    InvalidUser(Response.Status.BAD_REQUEST),
 +    InvalidExternalResource(Response.Status.BAD_REQUEST),
 +    InvalidNotification(Response.Status.BAD_REQUEST),
 +    InvalidPropagationTask(Response.Status.BAD_REQUEST),
 +    InvalidSchedTask(Response.Status.BAD_REQUEST),
 +    InvalidSyncTask(Response.Status.BAD_REQUEST),
 +    InvalidValues(Response.Status.BAD_REQUEST),
 +    NotFound(Response.Status.NOT_FOUND),
 +    RejectedUserCreate(Response.Status.BAD_REQUEST),
 +    RequiredValuesMissing(Response.Status.BAD_REQUEST),
 +    RESTValidation(Response.Status.BAD_REQUEST),
 +    GroupOwnership(Response.Status.BAD_REQUEST),
 +    Scheduling(Response.Status.BAD_REQUEST),
 +    DelegatedAdministration(Response.Status.FORBIDDEN),
 +    UnallowedSchemas(Response.Status.BAD_REQUEST),
 +    Unknown(Response.Status.BAD_REQUEST),
 +    Workflow(Response.Status.BAD_REQUEST);
 +
 +    private final Response.Status responseStatus;
 +
-     private ClientExceptionType(final Response.Status responseStatus) {
++    ClientExceptionType(final Response.Status responseStatus) {
 +        this.responseStatus = responseStatus;
 +    }
 +
 +    public static ClientExceptionType fromHeaderValue(final String 
exceptionTypeHeaderValue) {
 +        ClientExceptionType result = null;
 +        for (ClientExceptionType type : values()) {
 +            if (exceptionTypeHeaderValue.equals(type.name())) {
 +                result = type;
 +            }
 +        }
 +
 +        if (result == null) {
 +            throw new IllegalArgumentException("Unexpected header value: " + 
exceptionTypeHeaderValue);
 +        }
 +
 +        return result;
 +    }
 +
 +    public String getInfoHeaderValue(final String value) {
 +        return name() + ":" + value;
 +    }
 +
 +    public Response.Status getResponseStatus() {
 +        return responseStatus;
 +    }
 +
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/4e0783e1/common/lib/src/main/java/org/apache/syncope/common/lib/types/IntMappingType.java
----------------------------------------------------------------------
diff --cc 
common/lib/src/main/java/org/apache/syncope/common/lib/types/IntMappingType.java
index 4588a36,0000000..eccb89d
mode 100644,000000..100644
--- 
a/common/lib/src/main/java/org/apache/syncope/common/lib/types/IntMappingType.java
+++ 
b/common/lib/src/main/java/org/apache/syncope/common/lib/types/IntMappingType.java
@@@ -1,201 -1,0 +1,201 @@@
 +/*
 + * 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.common.lib.types;
 +
 +import java.util.Collection;
 +import java.util.EnumSet;
 +import java.util.HashSet;
 +import java.util.Set;
 +
 +import javax.xml.bind.annotation.XmlEnum;
 +
 +/**
 + * Internal attribute mapping type.
 + */
 +@XmlEnum
 +public enum IntMappingType {
 +
 +    // Unfortunately enum type cannot be extended ...
 +    // -------------------------
 +    // User attribute types (the same in UserMappingType)
 +    // -------------------------
 +    UserPlainSchema(AnyTypeKind.USER),
 +    UserDerivedSchema(AnyTypeKind.USER),
 +    UserVirtualSchema(AnyTypeKind.USER),
 +    UserKey(AnyTypeKind.USER),
 +    Username(AnyTypeKind.USER),
 +    Password(AnyTypeKind.USER),
 +    // -------------------------
 +    // Group attribute types (the same in GroupMappingType)
 +    // -------------------------
 +    GroupPlainSchema(AnyTypeKind.GROUP),
 +    GroupDerivedSchema(AnyTypeKind.GROUP),
 +    GroupVirtualSchema(AnyTypeKind.GROUP),
 +    GroupKey(AnyTypeKind.GROUP),
 +    GroupName(AnyTypeKind.GROUP),
 +    GroupOwnerSchema(AnyTypeKind.GROUP),
 +    // -------------------------
 +    // Any attribute types (the same in AnyMappingType)
 +    // -------------------------
 +    AnyObjectPlainSchema(AnyTypeKind.ANY_OBJECT),
 +    AnyObjectDerivedSchema(AnyTypeKind.ANY_OBJECT),
 +    AnyObjectVirtualSchema(AnyTypeKind.ANY_OBJECT),
 +    AnyObjectKey(AnyTypeKind.ANY_OBJECT);
 +
 +    private final AnyTypeKind anyTypeKind;
 +
-     private IntMappingType(final AnyTypeKind anyTypeKind) {
++    IntMappingType(final AnyTypeKind anyTypeKind) {
 +        this.anyTypeKind = anyTypeKind;
 +    }
 +
 +    public AnyTypeKind getAnyTypeKind() {
 +        return anyTypeKind;
 +    }
 +
 +    /**
 +     * Get attribute types for a certain any object type.
 +     *
 +     * @param anyTypeKind any object type
 +     * @param toBeFiltered types to be filtered from the result.
 +     * @return set of attribute types.
 +     */
 +    public static Set<IntMappingType> getAttributeTypes(
 +            final AnyTypeKind anyTypeKind, final Collection<IntMappingType> 
toBeFiltered) {
 +
 +        final Set<IntMappingType> res = getAttributeTypes(anyTypeKind);
 +        res.removeAll(toBeFiltered);
 +
 +        return res;
 +    }
 +
 +    /**
 +     * Get attribute types for a certain any object type.
 +     *
 +     * @param anyTypeKind any object type
 +     * @return set of attribute types.
 +     */
 +    public static Set<IntMappingType> getAttributeTypes(final AnyTypeKind 
anyTypeKind) {
 +        EnumSet<?> enumset;
 +
 +        switch (anyTypeKind) {
 +            case GROUP:
 +                enumset = EnumSet.allOf(GroupMappingType.class);
 +                break;
 +
 +            case ANY_OBJECT:
 +                enumset = EnumSet.allOf(AnyMappingType.class);
 +                break;
 +
 +            case USER:
 +            default:
 +                enumset = EnumSet.allOf(UserMappingType.class);
 +                break;
 +        }
 +
 +        final Set<IntMappingType> result = new HashSet<>(enumset.size());
 +        for (Object obj : enumset) {
 +            result.add(IntMappingType.valueOf(obj.toString()));
 +        }
 +
 +        return result;
 +    }
 +
 +    public static Set<IntMappingType> getEmbedded() {
 +        return EnumSet.of(IntMappingType.UserKey, IntMappingType.Username, 
IntMappingType.Password,
 +                IntMappingType.GroupKey, IntMappingType.GroupName, 
IntMappingType.GroupOwnerSchema,
 +                IntMappingType.AnyObjectKey);
 +    }
 +
 +    /**
 +     * Check if attribute type belongs to the specified any object type set.
 +     *
 +     * @param anyTypeKind any object type.
 +     * @param type attribute type.
 +     * @return true if attribute type belongs to the specified any object 
type set.
 +     */
 +    public static boolean contains(final AnyTypeKind anyTypeKind, final 
String type) {
 +        switch (anyTypeKind) {
 +            case GROUP:
 +                for (GroupMappingType c : GroupMappingType.values()) {
 +                    if (c.name().equals(type)) {
 +                        return true;
 +                    }
 +                }
 +                break;
 +
 +            case ANY_OBJECT:
 +                for (AnyMappingType c : AnyMappingType.values()) {
 +                    if (c.name().equals(type)) {
 +                        return true;
 +                    }
 +                }
 +                break;
 +
 +            case USER:
 +            default:
 +                for (UserMappingType c : UserMappingType.values()) {
 +                    if (c.name().equals(type)) {
 +                        return true;
 +                    }
 +                }
 +                break;
 +        }
 +        return false;
 +    }
 +
 +    /**
 +     * User attribute types.
 +     */
 +    private enum UserMappingType {
 +
 +        UserPlainSchema,
 +        UserDerivedSchema,
 +        UserVirtualSchema,
 +        UserKey,
 +        Username,
 +        Password;
 +
 +    }
 +
 +    /**
 +     * Group attribute types.
 +     */
 +    private enum GroupMappingType {
 +
 +        GroupPlainSchema,
 +        GroupDerivedSchema,
 +        GroupVirtualSchema,
 +        GroupKey,
 +        GroupName,
 +        GroupOwnerSchema;
 +
 +    }
 +
 +    /**
 +     * Any attribute types.
 +     */
 +    private enum AnyMappingType {
 +
 +        AnyObjectPlainSchema,
 +        AnyObjectDerivedSchema,
 +        AnyObjectVirtualSchema,
 +        AnyObjectKey;
 +
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/4e0783e1/common/lib/src/main/java/org/apache/syncope/common/lib/types/TaskType.java
----------------------------------------------------------------------
diff --cc 
common/lib/src/main/java/org/apache/syncope/common/lib/types/TaskType.java
index a60a5af,0000000..ce1b5cd
mode 100644,000000..100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/TaskType.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/TaskType.java
@@@ -1,54 -1,0 +1,54 @@@
 +/*
 + * 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.common.lib.types;
 +
 +import javax.xml.bind.annotation.XmlEnum;
 +
 +@XmlEnum
 +public enum TaskType {
 +
 +    PROPAGATION("propagation"),
 +    NOTIFICATION("notification"),
 +    SCHEDULED("sched"),
 +    SYNCHRONIZATION("sync"),
 +    PUSH("push");
 +
 +    private final String name;
 +
-     private TaskType(final String name) {
++    TaskType(final String name) {
 +        this.name = name;
 +    }
 +
 +    @Override
 +    public String toString() {
 +        return name;
 +    }
 +
 +    public static TaskType fromString(final String name) {
 +        if (name != null) {
 +            for (TaskType t : TaskType.values()) {
 +                if (t.name.equalsIgnoreCase(name)) {
 +                    return t;
 +                }
 +            }
 +        }
 +
 +        return null;
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/4e0783e1/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/Preference.java
----------------------------------------------------------------------
diff --cc 
common/rest-api/src/main/java/org/apache/syncope/common/rest/api/Preference.java
index a448c03,0000000..8d21e54
mode 100644,000000..100644
--- 
a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/Preference.java
+++ 
b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/Preference.java
@@@ -1,59 -1,0 +1,59 @@@
 +/*
 + * 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.common.rest.api;
 +
 +/**
 + * Preferences available to be specified during requests.
 + *
 + * @see RESTHeaders#PREFER
 + * @see RESTHeaders#PREFERENCE_APPLIED
 + */
 +public enum Preference {
 +
 +    NONE(""),
 +    RETURN_CONTENT("return-content"),
 +    RETURN_NO_CONTENT("return-no-content");
 +
 +    private final String literal;
 +
-     private Preference(final String literal) {
++    Preference(final String literal) {
 +        this.literal = literal;
 +    }
 +
 +    @Override
 +    public String toString() {
 +        return literal;
 +    }
 +
 +    public static Preference fromString(final String literal) {
 +        Preference result = null;
 +
 +        for (Preference preference : values()) {
 +            if (preference.toString().equalsIgnoreCase(literal)) {
 +                result = preference;
 +            }
 +        }
 +
 +        if (result == null) {
 +            result = NONE;
 +        }
 +
 +        return result;
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/4e0783e1/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractAnyLogic.java
----------------------------------------------------------------------
diff --cc 
core/logic/src/main/java/org/apache/syncope/core/logic/AbstractAnyLogic.java
index e85058c,0000000..9a796ef
mode 100644,000000..100644
--- 
a/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractAnyLogic.java
+++ 
b/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractAnyLogic.java
@@@ -1,259 -1,0 +1,259 @@@
 +/*
 + * 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.core.logic;
 +
 +import java.util.ArrayList;
 +import java.util.Collection;
 +import java.util.HashSet;
 +import java.util.List;
 +import java.util.Set;
 +import org.apache.commons.collections4.CollectionUtils;
 +import org.apache.commons.collections4.Predicate;
 +import org.apache.commons.lang3.tuple.ImmutablePair;
 +import org.apache.commons.lang3.tuple.Pair;
 +import org.apache.syncope.common.lib.SyncopeClientException;
 +import org.apache.syncope.common.lib.patch.AnyPatch;
 +import org.apache.syncope.common.lib.to.AnyTO;
 +import org.apache.syncope.common.lib.to.GroupTO;
 +import org.apache.syncope.common.lib.to.UserTO;
 +import org.apache.syncope.common.lib.types.AnyTypeKind;
 +import org.apache.syncope.common.lib.types.ClientExceptionType;
 +import org.apache.syncope.core.misc.RealmUtils;
 +import org.apache.syncope.core.misc.TemplateUtils;
 +import org.apache.syncope.core.misc.security.DelegatedAdministrationException;
 +import org.apache.syncope.core.misc.spring.ApplicationContextProvider;
 +import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
 +import org.apache.syncope.core.persistence.api.dao.RealmDAO;
 +import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 +import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 +import org.apache.syncope.core.persistence.api.entity.AnyType;
 +import org.apache.syncope.core.persistence.api.entity.Realm;
 +import org.apache.syncope.core.provisioning.api.LogicActions;
 +import org.springframework.beans.factory.annotation.Autowired;
 +import org.springframework.beans.factory.support.AbstractBeanDefinition;
 +
 +public abstract class AbstractAnyLogic<TO extends AnyTO, P extends AnyPatch>
 +        extends AbstractResourceAssociator<TO> {
 +
 +    @Autowired
 +    private RealmDAO realmDAO;
 +
 +    @Autowired
 +    private AnyTypeDAO anyTypeDAO;
 +
 +    @Autowired
 +    private TemplateUtils templateUtils;
 +
 +    private List<LogicActions> getActions(final Realm realm) {
 +        List<LogicActions> actions = new ArrayList<>();
 +
 +        for (String className : realm.getActionsClassNames()) {
 +            try {
 +                Class<?> actionsClass = Class.forName(className);
 +                LogicActions logicActions = (LogicActions) 
ApplicationContextProvider.getBeanFactory().
 +                        createBean(actionsClass, 
AbstractBeanDefinition.AUTOWIRE_BY_TYPE, true);
 +
 +                actions.add(logicActions);
 +            } catch (Exception e) {
 +                LOG.warn("Class '{}' not found", className, e);
 +            }
 +        }
 +
 +        return actions;
 +    }
 +
 +    protected Pair<TO, List<LogicActions>> beforeCreate(final TO input) {
 +        Realm realm = realmDAO.find(input.getRealm());
 +        if (realm == null) {
 +            SyncopeClientException sce = 
SyncopeClientException.build(ClientExceptionType.InvalidRealm);
 +            sce.getElements().add(input.getRealm());
 +            throw sce;
 +        }
 +
 +        AnyType anyType = input instanceof UserTO
 +                ? anyTypeDAO.findUser()
 +                : input instanceof GroupTO
 +                        ? anyTypeDAO.findGroup()
 +                        : anyTypeDAO.find(input.getType());
 +        if (anyType == null) {
 +            SyncopeClientException sce = 
SyncopeClientException.build(ClientExceptionType.InvalidAnyType);
 +            sce.getElements().add(input.getType());
 +            throw sce;
 +        }
 +
 +        TO any = input;
 +
 +        templateUtils.apply(any, realm.getTemplate(anyType));
 +
 +        List<LogicActions> actions = getActions(realm);
 +        for (LogicActions action : actions) {
 +            any = action.beforeCreate(any);
 +        }
 +
 +        LOG.debug("Input: {}\nOutput: {}\n", input, any);
 +
 +        return ImmutablePair.of(any, actions);
 +    }
 +
 +    protected TO afterCreate(final TO input, final List<LogicActions> 
actions) {
 +        TO any = input;
 +
 +        for (LogicActions action : actions) {
 +            any = action.afterCreate(any);
 +        }
 +
 +        return any;
 +    }
 +
 +    protected Pair<P, List<LogicActions>> beforeUpdate(final P input, final 
String realmPath) {
 +        Realm realm = realmDAO.find(realmPath);
 +        if (realm == null) {
 +            SyncopeClientException sce = 
SyncopeClientException.build(ClientExceptionType.InvalidRealm);
 +            sce.getElements().add(realmPath);
 +            throw sce;
 +        }
 +
 +        P mod = input;
 +
 +        List<LogicActions> actions = getActions(realm);
 +        for (LogicActions action : actions) {
 +            mod = action.beforeUpdate(mod);
 +        }
 +
 +        LOG.debug("Input: {}\nOutput: {}\n", input, mod);
 +
 +        return ImmutablePair.of(mod, actions);
 +    }
 +
 +    protected TO afterUpdate(final TO input, final List<LogicActions> 
actions) {
 +        TO any = input;
 +
 +        for (LogicActions action : actions) {
 +            any = action.afterUpdate(any);
 +        }
 +
 +        return any;
 +    }
 +
 +    protected Pair<TO, List<LogicActions>> beforeDelete(final TO input) {
 +        Realm realm = realmDAO.find(input.getRealm());
 +        if (realm == null) {
 +            SyncopeClientException sce = 
SyncopeClientException.build(ClientExceptionType.InvalidRealm);
 +            sce.getElements().add(input.getRealm());
 +            throw sce;
 +        }
 +
 +        TO any = input;
 +
 +        List<LogicActions> actions = getActions(realm);
 +        for (LogicActions action : actions) {
 +            any = action.beforeDelete(any);
 +        }
 +
 +        LOG.debug("Input: {}\nOutput: {}\n", input, any);
 +
 +        return ImmutablePair.of(any, actions);
 +    }
 +
 +    protected TO afterDelete(final TO input, final List<LogicActions> 
actions) {
 +        TO any = input;
 +
 +        for (LogicActions action : actions) {
 +            any = action.afterDelete(any);
 +        }
 +
 +        return any;
 +    }
 +
 +    private static class StartsWithPredicate implements Predicate<String> {
 +
 +        private final Collection<String> targets;
 +
-         public StartsWithPredicate(final Collection<String> targets) {
++        StartsWithPredicate(final Collection<String> targets) {
 +            this.targets = targets;
 +        }
 +
 +        @Override
 +        public boolean evaluate(final String realm) {
 +            return CollectionUtils.exists(targets, new Predicate<String>() {
 +
 +                @Override
 +                public boolean evaluate(final String target) {
 +                    return realm.startsWith(target);
 +                }
 +            });
 +        }
 +
 +    }
 +
 +    protected Set<String> getEffectiveRealms(
 +            final Set<String> allowedRealms, final Collection<String> 
requestedRealms) {
 +
 +        Set<String> allowed = RealmUtils.normalize(allowedRealms);
 +        Set<String> requested = RealmUtils.normalize(requestedRealms);
 +
 +        Set<String> effective = new HashSet<>();
 +        CollectionUtils.select(requested, new StartsWithPredicate(allowed), 
effective);
 +        CollectionUtils.select(allowed, new StartsWithPredicate(requested), 
effective);
 +
 +        return effective;
 +    }
 +
 +    protected void securityChecks(final Set<String> effectiveRealms, final 
String realm, final Long key) {
 +        if (!CollectionUtils.exists(effectiveRealms, new Predicate<String>() {
 +
 +            @Override
 +            public boolean evaluate(final String ownedRealm) {
 +                return realm.startsWith(ownedRealm);
 +            }
 +        })) {
 +
 +            throw new DelegatedAdministrationException(
 +                    this instanceof UserLogic
 +                            ? AnyTypeKind.USER
 +                            : this instanceof GroupLogic
 +                                    ? AnyTypeKind.GROUP
 +                                    : AnyTypeKind.ANY_OBJECT,
 +                    key);
 +        }
 +    }
 +
 +    public abstract TO read(Long key);
 +
 +    public abstract int count(List<String> realms);
 +
 +    public abstract TO create(TO anyTO);
 +
 +    public abstract TO update(P anyPatch);
 +
 +    public abstract TO delete(Long key);
 +
 +    public abstract List<TO> list(
 +            int page, int size, List<OrderByClause> orderBy,
 +            List<String> realms,
 +            boolean details);
 +
 +    public abstract List<TO> search(
 +            SearchCond searchCondition,
 +            int page, int size, List<OrderByClause> orderBy,
 +            List<String> realms,
 +            boolean details);
 +
 +    public abstract int searchCount(SearchCond searchCondition, List<String> 
realms);
 +}

Reply via email to