You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by jo...@apache.org on 2014/09/20 16:54:25 UTC

[07/14] git commit: AMBARI-7349 - Admin : LDAP Sync API to support sync events

AMBARI-7349 - Admin : LDAP Sync API to support sync events


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/1f7dbd5d
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/1f7dbd5d
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/1f7dbd5d

Branch: refs/heads/branch-alerts-dev
Commit: 1f7dbd5df4b624a083cc8f8fe4141e6289ecf46e
Parents: 21d784b
Author: tbeerbower <tb...@hortonworks.com>
Authored: Thu Sep 18 22:12:45 2014 -0400
Committer: tbeerbower <tb...@hortonworks.com>
Committed: Fri Sep 19 14:25:08 2014 -0400

----------------------------------------------------------------------
 .../LdapSyncEventResourceDefinition.java        |  57 +++
 .../resources/ResourceInstanceFactoryImpl.java  |   4 +
 .../api/services/LdapSyncEventService.java      | 144 ++++++
 .../internal/DefaultProviderModule.java         |   2 +
 .../internal/LdapSyncEventResourceProvider.java | 438 +++++++++++++++++++
 .../ambari/server/controller/spi/Resource.java  |   4 +-
 .../orm/entities/LdapSyncEventEntity.java       | 284 ++++++++++++
 .../server/orm/entities/LdapSyncSpecEntity.java | 143 ++++++
 .../LdapSyncEventResourceDefinitionTest.java    |  49 +++
 .../api/services/LdapSyncEventServiceTest.java  |  97 ++++
 .../LdapSyncEventResourceProviderTest.java      | 114 +++++
 .../orm/entities/LdapSyncEventEntityTest.java   | 166 +++++++
 .../orm/entities/LdapSyncSpecEntityTest.java    |  95 ++++
 13 files changed, 1596 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/1f7dbd5d/ambari-server/src/main/java/org/apache/ambari/server/api/resources/LdapSyncEventResourceDefinition.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/LdapSyncEventResourceDefinition.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/LdapSyncEventResourceDefinition.java
new file mode 100644
index 0000000..554c688
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/LdapSyncEventResourceDefinition.java
@@ -0,0 +1,57 @@
+/**
+ * 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.ambari.server.api.resources;
+
+import org.apache.ambari.server.controller.spi.Resource;
+
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * LDAP sync event resource definition.
+ */
+public class LdapSyncEventResourceDefinition extends BaseResourceDefinition {
+
+  // ----- Constructors ------------------------------------------------------
+
+  /**
+   * Construct an event resource definition.
+   */
+  public LdapSyncEventResourceDefinition() {
+    super(Resource.Type.LdapSyncEvent);
+  }
+
+
+  // ----- ResourceDefinition ------------------------------------------------
+
+  @Override
+  public String getPluralName() {
+    return "ldap_sync_events";
+  }
+
+  @Override
+  public String getSingularName() {
+    return "ldap_sync_event";
+  }
+
+  @Override
+  public Set<SubResourceDefinition> getSubResourceDefinitions() {
+    return Collections.emptySet();
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/1f7dbd5d/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java
index f85c0ea..2676fd3 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java
@@ -280,6 +280,10 @@ public class ResourceInstanceFactoryImpl implements ResourceInstanceFactory {
         resourceDefinition = new ClientConfigResourceDefinition();
         break;
 
+      case LdapSyncEvent:
+        resourceDefinition = new LdapSyncEventResourceDefinition();
+        break;
+
       default:
         throw new IllegalArgumentException("Unsupported resource type: " + type);
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/1f7dbd5d/ambari-server/src/main/java/org/apache/ambari/server/api/services/LdapSyncEventService.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/LdapSyncEventService.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/LdapSyncEventService.java
new file mode 100644
index 0000000..bf4bcff
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/LdapSyncEventService.java
@@ -0,0 +1,144 @@
+/**
+ * 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.ambari.server.api.services;
+
+import org.apache.ambari.server.api.resources.ResourceInstance;
+import org.apache.ambari.server.controller.spi.Resource;
+
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import java.util.Collections;
+
+/**
+ * Service responsible for ldap sync event resource requests.
+ */
+@Path("/ldap_sync_events/")
+public class LdapSyncEventService extends BaseService {
+  /**
+   * Handles: GET /ldap_sync_events/{eventId}
+   * Get a specific view.
+   *
+   * @param headers   http headers
+   * @param ui        uri info
+   * @param eventId   event id
+   *
+   * @return view instance representation
+   */
+  @GET
+  @Path("{eventId}")
+  @Produces("text/plain")
+  public Response getEvent(@Context HttpHeaders headers, @Context UriInfo ui,
+                          @PathParam("eventId") String eventId) {
+
+    return handleRequest(headers, null, ui, Request.Type.GET, createEventResource(eventId));
+  }
+
+  /**
+   * Handles: GET  /ldap_sync_events
+   * Get all events.
+   *
+   * @param headers  http headers
+   * @param ui       uri info
+   *
+   * @return view collection resource representation
+   */
+  @GET
+  @Produces("text/plain")
+  public Response getEvents(@Context HttpHeaders headers, @Context UriInfo ui) {
+    return handleRequest(headers, null, ui, Request.Type.GET, createEventResource(null));
+  }
+
+  /**
+   * Handles: POST /ldap_sync_events/
+   * Create an event.
+   *
+   * @param headers    http headers
+   * @param ui         uri info
+   *
+   * @return information regarding the created view
+   */
+  @POST
+  @Produces("text/plain")
+  public Response createEvent(String body, @Context HttpHeaders headers, @Context UriInfo ui) {
+
+    return handleRequest(headers, body, ui, Request.Type.POST, createEventResource(null));
+  }
+
+  /**
+   * Handles: PUT /ldap_sync_events/{eventId}
+   * Update a specific event.
+   *
+   * @param headers  http headers
+   * @param ui       uri info
+   * @param eventId  event id
+   *
+   * @return information regarding the updated event
+   */
+  @PUT
+  @Path("{eventId}")
+  @Produces("text/plain")
+  public Response updateEvent(String body, @Context HttpHeaders headers, @Context UriInfo ui,
+                             @PathParam("eventId") String eventId) {
+
+    return handleRequest(headers, body, ui, Request.Type.PUT, createEventResource(eventId));
+  }
+
+  /**
+   * Handles: DELETE /ldap_sync_events/{eventId}
+   * Delete a specific view.
+   *
+   * @param headers  http headers
+   * @param ui       uri info
+   * @param eventId  event id
+   *
+   * @return information regarding the deleted event
+   */
+  @DELETE
+  @Path("{eventId}")
+  @Produces("text/plain")
+  public Response deleteEvent(@Context HttpHeaders headers, @Context UriInfo ui,
+                             @PathParam("eventId") String eventId) {
+
+    return handleRequest(headers, null, ui, Request.Type.DELETE, createEventResource(eventId));
+  }
+
+
+  // ----- helper methods ----------------------------------------------------
+
+  /**
+   * Create a view resource.
+   *
+   * @param eventId the event id
+   *
+   * @return an event resource instance
+   */
+  private ResourceInstance createEventResource(String eventId) {
+    return createResource(Resource.Type.LdapSyncEvent,
+        Collections.singletonMap(Resource.Type.LdapSyncEvent, eventId));
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/1f7dbd5d/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java
index 1b5075a..99ac0f1 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java
@@ -79,6 +79,8 @@ public class DefaultProviderModule extends AbstractProviderModule {
         return new ViewPermissionResourceProvider();
       case ClusterPrivilege:
         return new ClusterPrivilegeResourceProvider();
+      case LdapSyncEvent:
+        return new LdapSyncEventResourceProvider();
       default:
         return AbstractControllerResourceProvider.getResourceProvider(type, propertyIds,
             keyPropertyIds, managementController);

http://git-wip-us.apache.org/repos/asf/ambari/blob/1f7dbd5d/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/LdapSyncEventResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/LdapSyncEventResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/LdapSyncEventResourceProvider.java
new file mode 100644
index 0000000..188c071
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/LdapSyncEventResourceProvider.java
@@ -0,0 +1,438 @@
+/**
+ * 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.ambari.server.controller.internal;
+
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.controller.spi.NoSuchParentResourceException;
+import org.apache.ambari.server.controller.spi.NoSuchResourceException;
+import org.apache.ambari.server.controller.spi.Predicate;
+import org.apache.ambari.server.controller.spi.Request;
+import org.apache.ambari.server.controller.spi.RequestStatus;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.spi.ResourceAlreadyExistsException;
+import org.apache.ambari.server.controller.spi.SystemException;
+import org.apache.ambari.server.controller.spi.UnsupportedPropertyException;
+import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.apache.ambari.server.orm.entities.LdapSyncEventEntity;
+import org.apache.ambari.server.orm.entities.LdapSyncSpecEntity;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+import java.util.concurrent.ConcurrentSkipListMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Resource provider for ldap sync events.
+ */
+public class LdapSyncEventResourceProvider extends AbstractResourceProvider {
+
+  /**
+   * Thread pool
+   */
+  private static ExecutorService executorService;
+
+  // TODO : Do we need to expose the thread pool params in configuration?
+  private final static int  THREAD_POOL_CORE_SIZE = 2;
+  private final static int  THREAD_POOL_MAX_SIZE  = 5;
+  private final static long THREAD_POOL_TIMEOUT   = 1000L;
+
+  /**
+   * Event property id constants.
+   */
+  public static final String EVENT_ID_PROPERTY_ID            = "Event/id";
+  public static final String EVENT_STATUS_PROPERTY_ID        = "Event/status";
+  public static final String EVENT_STATUS_DETAIL_PROPERTY_ID = "Event/status_detail";
+  public static final String EVENT_START_TIME_PROPERTY_ID    = "Event/sync_start_time";
+  public static final String EVENT_END_TIME_PROPERTY_ID      = "Event/sync_end_time";
+  public static final String USERS_FETCHED_PROPERTY_ID       = "Event/results/users_fetched";
+  public static final String USERS_CREATED_PROPERTY_ID       = "Event/results/users_created";
+  public static final String USERS_UPDATED_PROPERTY_ID       = "Event/results/users_updated";
+  public static final String USERS_REMOVED_PROPERTY_ID       = "Event/results/users_removed";
+  public static final String GROUPS_FETCHED_PROPERTY_ID      = "Event/results/groups_fetched";
+  public static final String GROUPS_CREATED_PROPERTY_ID      = "Event/results/groups_created";
+  public static final String GROUPS_UPDATED_PROPERTY_ID      = "Event/results/groups_updated";
+  public static final String GROUPS_REMOVED_PROPERTY_ID      = "Event/results/groups_removed";
+  public static final String MEMBERSHIPS_FETCHED_PROPERTY_ID = "Event/results/memberships_fetched";
+  public static final String MEMBERSHIPS_CREATED_PROPERTY_ID = "Event/results/memberships_created";
+  public static final String MEMBERSHIPS_UPDATED_PROPERTY_ID = "Event/results/memberships_updated";
+  public static final String EVENT_SPECS_PROPERTY_ID         = "Event/specs";
+
+  /**
+   * The key property ids for a event resource.
+   */
+  private static Map<Resource.Type, String> keyPropertyIds = new HashMap<Resource.Type, String>();
+  static {
+    keyPropertyIds.put(Resource.Type.LdapSyncEvent, EVENT_ID_PROPERTY_ID);
+  }
+
+  /**
+   * The property ids for a event resource.
+   */
+  private static Set<String> propertyIds = new HashSet<String>();
+
+  static {
+    propertyIds.add(EVENT_ID_PROPERTY_ID);
+    propertyIds.add(EVENT_STATUS_PROPERTY_ID);
+    propertyIds.add(EVENT_STATUS_DETAIL_PROPERTY_ID);
+    propertyIds.add(EVENT_START_TIME_PROPERTY_ID);
+    propertyIds.add(EVENT_END_TIME_PROPERTY_ID);
+    propertyIds.add(USERS_FETCHED_PROPERTY_ID);
+    propertyIds.add(USERS_CREATED_PROPERTY_ID);
+    propertyIds.add(USERS_UPDATED_PROPERTY_ID);
+    propertyIds.add(USERS_REMOVED_PROPERTY_ID);
+    propertyIds.add(GROUPS_FETCHED_PROPERTY_ID);
+    propertyIds.add(GROUPS_CREATED_PROPERTY_ID);
+    propertyIds.add(GROUPS_UPDATED_PROPERTY_ID);
+    propertyIds.add(GROUPS_REMOVED_PROPERTY_ID);
+    propertyIds.add(MEMBERSHIPS_FETCHED_PROPERTY_ID);
+    propertyIds.add(MEMBERSHIPS_CREATED_PROPERTY_ID);
+    propertyIds.add(MEMBERSHIPS_UPDATED_PROPERTY_ID);
+    propertyIds.add(EVENT_SPECS_PROPERTY_ID);
+  }
+
+  /**
+   * Spec property keys.
+   */
+  private static final String PRINCIPAL_TYPE_SPEC_KEY = "principal_type";
+  private static final String SYNC_TYPE_SPEC_KEY      = "sync_type";
+  private static final String NAMES_SPEC_KEY          = "names";
+
+  /**
+   * Map of all sync events.
+   */
+  private final Map<Long, LdapSyncEventEntity> events = new ConcurrentSkipListMap<Long, LdapSyncEventEntity>();
+
+  /**
+   * The queue of events to be processed.
+   */
+  private final Queue<LdapSyncEventEntity> eventQueue = new LinkedList<LdapSyncEventEntity>();
+
+  /**
+   * Indicates whether or not the events are currently being processed.
+   */
+  private volatile boolean processingEvents = false;
+
+  /**
+   * The next event id.
+   */
+  private AtomicLong nextEventId = new AtomicLong(1L);
+
+  /**
+   * The logger.
+   */
+  protected final static Logger LOG = LoggerFactory.getLogger(LdapSyncEventResourceProvider.class);
+
+
+  // ----- Constructors ------------------------------------------------------
+
+  /**
+   * Construct a event resource provider.
+   */
+  public LdapSyncEventResourceProvider() {
+    super(propertyIds, keyPropertyIds);
+  }
+
+
+  // ----- ResourceProvider --------------------------------------------------
+
+  @Override
+  public RequestStatus createResources(Request event)
+      throws SystemException, UnsupportedPropertyException,
+      ResourceAlreadyExistsException, NoSuchParentResourceException {
+    Set<LdapSyncEventEntity> newEvents = new HashSet<LdapSyncEventEntity>();
+
+    for (Map<String, Object> properties : event.getProperties()) {
+      newEvents.add(createResources(getCreateCommand(properties)));
+    }
+    notifyCreate(Resource.Type.ViewInstance, event);
+
+    Set<Resource> associatedResources = new HashSet<Resource>();
+    for (LdapSyncEventEntity eventEntity : newEvents) {
+      Resource resource = new ResourceImpl(Resource.Type.LdapSyncEvent);
+      resource.setProperty(EVENT_ID_PROPERTY_ID, eventEntity.getId());
+      associatedResources.add(resource);
+      synchronized (eventQueue) {
+        eventQueue.offer(eventEntity);
+      }
+    }
+
+    ensureEventProcessor();
+
+    return getRequestStatus(null, associatedResources);
+  }
+
+  @Override
+  public Set<Resource> getResources(Request event, Predicate predicate)
+      throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException {
+
+    Set<Resource> resources    = new HashSet<Resource>();
+    Set<String>   requestedIds = getRequestPropertyIds(event, predicate);
+
+    for (LdapSyncEventEntity eventEntity : events.values()) {
+      resources.add(toResource(eventEntity, requestedIds));
+    }
+    return resources;
+  }
+
+  @Override
+  public RequestStatus updateResources(Request event, Predicate predicate)
+      throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException {
+    throw new UnsupportedOperationException("Not supported.");
+  }
+
+  @Override
+  public RequestStatus deleteResources(Predicate predicate)
+      throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException {
+    modifyResources(getDeleteCommand(predicate));
+    notifyDelete(Resource.Type.ViewInstance, predicate);
+    return getRequestStatus(null);
+  }
+
+  @Override
+  public Map<Resource.Type, String> getKeyPropertyIds() {
+    return keyPropertyIds;
+  }
+
+
+  // ----- AbstractResourceProvider ------------------------------------------
+
+  @Override
+  protected Set<String> getPKPropertyIds() {
+    return new HashSet<String>(keyPropertyIds.values());
+  }
+
+
+  // ----- helper methods ----------------------------------------------------
+
+  /**
+   * Ensure that sync events are being processed.
+   */
+  protected void ensureEventProcessor() {
+
+    if (!processingEvents) {
+      synchronized (eventQueue) {
+        if (!processingEvents) {
+          processingEvents = true;
+          getExecutorService().submit(new Runnable() {
+            @Override
+            public void run() {
+              processSyncEvents();
+            }
+          });
+        }
+      }
+    }
+  }
+
+  // create a resource from the given event entity
+  private Resource toResource(LdapSyncEventEntity eventEntity, Set<String> requestedIds) {
+    Resource resource = new ResourceImpl(Resource.Type.LdapSyncEvent);
+
+    setResourceProperty(resource, EVENT_ID_PROPERTY_ID, eventEntity.getId(), requestedIds);
+    setResourceProperty(resource, EVENT_STATUS_PROPERTY_ID, eventEntity.getStatus().toString().toUpperCase(), requestedIds);
+    setResourceProperty(resource, EVENT_STATUS_DETAIL_PROPERTY_ID, eventEntity.getStatusDetail(), requestedIds);
+    setResourceProperty(resource, USERS_FETCHED_PROPERTY_ID, eventEntity.getUsersFetched(), requestedIds);
+    setResourceProperty(resource, USERS_CREATED_PROPERTY_ID, eventEntity.getUsersCreated(), requestedIds);
+    setResourceProperty(resource, USERS_UPDATED_PROPERTY_ID, eventEntity.getUsersUpdated(), requestedIds);
+    setResourceProperty(resource, USERS_REMOVED_PROPERTY_ID, eventEntity.getUsersRemoved(), requestedIds);
+    setResourceProperty(resource, GROUPS_FETCHED_PROPERTY_ID, eventEntity.getGroupsFetched(), requestedIds);
+    setResourceProperty(resource, GROUPS_CREATED_PROPERTY_ID, eventEntity.getGroupsCreated(), requestedIds);
+    setResourceProperty(resource, GROUPS_UPDATED_PROPERTY_ID, eventEntity.getGroupsUpdated(), requestedIds);
+    setResourceProperty(resource, GROUPS_REMOVED_PROPERTY_ID, eventEntity.getGroupsRemoved(), requestedIds);
+    setResourceProperty(resource, MEMBERSHIPS_FETCHED_PROPERTY_ID, eventEntity.getMembershipsFetched(), requestedIds);
+    setResourceProperty(resource, MEMBERSHIPS_CREATED_PROPERTY_ID, eventEntity.getMembershipsCreated(), requestedIds);
+    setResourceProperty(resource, MEMBERSHIPS_UPDATED_PROPERTY_ID, eventEntity.getMembershipsUpdated(), requestedIds);
+
+    Set<Map<String, String>> specs = new HashSet<Map<String, String>>();
+
+    List<LdapSyncSpecEntity> specList = eventEntity.getSpecs();
+
+    for (LdapSyncSpecEntity spec : specList) {
+
+      Map<String, String> specMap = new HashMap<String, String>();
+
+      specMap.put(PRINCIPAL_TYPE_SPEC_KEY, spec.getPrincipalType().toString().toLowerCase());
+      specMap.put(SYNC_TYPE_SPEC_KEY, spec.getSyncType().toString().toLowerCase());
+
+      List<String> names = spec.getPrincipalNames();
+
+      if (!names.isEmpty()) {
+        specMap.put(NAMES_SPEC_KEY, names.toString().replace("[", "").replace("]", "").replace(", ", ","));
+      }
+      specs.add(specMap);
+    }
+    setResourceProperty(resource, EVENT_SPECS_PROPERTY_ID, specs, requestedIds);
+
+    setResourceProperty(resource, EVENT_START_TIME_PROPERTY_ID, eventEntity.getStartTime(), requestedIds);
+    setResourceProperty(resource, EVENT_END_TIME_PROPERTY_ID, eventEntity.getEndTime(), requestedIds);
+
+    return resource;
+  }
+
+  // create a event entity from the given set of properties
+  private LdapSyncEventEntity toEntity(Map<String, Object> properties) {
+    LdapSyncEventEntity      entity   = new LdapSyncEventEntity(getNextEventId());
+    List<LdapSyncSpecEntity> specList = new LinkedList<LdapSyncSpecEntity>();
+
+    Set<Map<String, String>> specs = (Set<Map<String, String>>) properties.get(EVENT_SPECS_PROPERTY_ID);
+
+    for (Map<String, String> specMap : specs) {
+
+      LdapSyncSpecEntity.SyncType      syncType      = null;
+      LdapSyncSpecEntity.PrincipalType principalType = null;
+
+      List<String> principalNames = Collections.emptyList();
+
+      for (Map.Entry<String, String> entry : specMap.entrySet()) {
+        String key = entry.getKey();
+        if (key.equalsIgnoreCase(PRINCIPAL_TYPE_SPEC_KEY)) {
+          principalType = LdapSyncSpecEntity.PrincipalType.valueOfIgnoreCase(entry.getValue());
+
+        } else if (key.equalsIgnoreCase(SYNC_TYPE_SPEC_KEY)) {
+          syncType = LdapSyncSpecEntity.SyncType.valueOfIgnoreCase(entry.getValue());
+
+        } else if (key.equalsIgnoreCase(NAMES_SPEC_KEY)) {
+          String names = entry.getValue();
+          principalNames = Arrays.asList(names.split("\\s*,\\s*"));
+        } else {
+          throw new IllegalArgumentException("Unknown spec key " + key + ".");
+        }
+      }
+
+      if (syncType == null || principalType == null) {
+        throw new IllegalArgumentException("LDAP event spec must include both sync-type and principal-type.");
+      }
+
+      LdapSyncSpecEntity spec = new LdapSyncSpecEntity(principalType, syncType, principalNames);
+      specList.add(spec);
+    }
+    entity.setSpecs(specList);
+
+    return entity;
+  }
+
+  // get the next event id
+  private long getNextEventId() {
+    return nextEventId.getAndIncrement();
+  }
+
+  // Create a create command with all properties set
+  private Command<LdapSyncEventEntity> getCreateCommand(final Map<String, Object> properties) {
+    return new Command<LdapSyncEventEntity>() {
+      @Override
+      public LdapSyncEventEntity invoke() throws AmbariException {
+
+        LdapSyncEventEntity eventEntity = toEntity(properties);
+
+        events.put(eventEntity.getId(), eventEntity);
+
+        return eventEntity;
+      }
+    };
+  }
+
+  // Create a delete command with the given predicate
+  private Command<Void> getDeleteCommand(final Predicate predicate) {
+    return new Command<Void>() {
+      @Override
+      public Void invoke() throws AmbariException {
+        Set<String>  requestedIds = getRequestPropertyIds(PropertyHelper.getReadRequest(), predicate);
+
+        Set<LdapSyncEventEntity> entities = new HashSet<LdapSyncEventEntity>();
+
+        for (LdapSyncEventEntity entity : events.values()){
+              Resource resource = toResource(entity, requestedIds);
+              if (predicate == null || predicate.evaluate(resource)) {
+                entities.add(entity);
+              }
+        }
+        for (LdapSyncEventEntity entity : entities) {
+          events.remove(entity.getId());
+        }
+        return null;
+      }
+    };
+  }
+
+  // Get the ldap sync thread pool
+  private static synchronized ExecutorService getExecutorService() {
+    if (executorService == null) {
+      LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
+
+      ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
+          THREAD_POOL_CORE_SIZE,
+          THREAD_POOL_MAX_SIZE,
+          THREAD_POOL_TIMEOUT,
+          TimeUnit.MILLISECONDS,
+          queue);
+
+      threadPoolExecutor.allowCoreThreadTimeOut(true);
+      executorService = threadPoolExecutor;
+    }
+    return executorService;
+  }
+
+  // Process any queued up sync events
+  private void processSyncEvents() {
+
+    while (processingEvents) {
+      LdapSyncEventEntity event;
+      synchronized (eventQueue) {
+        event = eventQueue.poll();
+        if (event == null) {
+          processingEvents = false;
+          return;
+        }
+      }
+
+      event.setStatus(LdapSyncEventEntity.Status.Running);
+      event.setStatusDetail("Running LDAP sync.");
+      event.setStartTime(System.currentTimeMillis());
+
+      try {
+
+        // TODO : call back end code to perform LDAP sync
+
+        // TODO : update event resource with results of sync
+
+        event.setEndTime(System.currentTimeMillis());
+        event.setStatus(LdapSyncEventEntity.Status.Complete);
+        event.setStatusDetail("Completed LDAP sync.");
+      } catch (Exception e) {
+        String msg = "Caught exception running LDAP sync. ";
+        event.setStatus(LdapSyncEventEntity.Status.Error);
+        event.setStatusDetail(msg + e.getMessage());
+        LOG.error(msg, e);
+      }
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/1f7dbd5d/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java
index a14f1f0..6cf34ca 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java
@@ -121,7 +121,8 @@ public interface Resource {
     ViewPermission,
     Controller,
     ClientConfig,
-    StackLevelConfiguration;
+    StackLevelConfiguration,
+    LdapSyncEvent;
 
     /**
      * Get the {@link Type} that corresponds to this InternalType.
@@ -204,6 +205,7 @@ public interface Resource {
     public static final Type Controller = InternalType.Controller.getType();
     public static final Type ClientConfig = InternalType.ClientConfig.getType();
     public static final Type StackLevelConfiguration = InternalType.StackLevelConfiguration.getType();
+    public static final Type LdapSyncEvent = InternalType.LdapSyncEvent.getType();
 
     /**
      * The type name.

http://git-wip-us.apache.org/repos/asf/ambari/blob/1f7dbd5d/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/LdapSyncEventEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/LdapSyncEventEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/LdapSyncEventEntity.java
new file mode 100644
index 0000000..9ce3ee0
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/LdapSyncEventEntity.java
@@ -0,0 +1,284 @@
+/**
+ * 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.ambari.server.orm.entities;
+
+import java.util.List;
+
+/**
+ * LDAP sync event entity.
+ */
+public class LdapSyncEventEntity {
+
+  /**
+   * The unique id.
+   */
+  private final long id;
+
+  /**
+   * The status of this event.
+   */
+  private Status status;
+
+  /**
+   * The detail status message for this event.
+   */
+  private String statusDetail;
+
+  /**
+   * Sync event times.
+   */
+  private long startTime;
+  private long endTime;
+
+  /**
+   * Results of sync event.
+   */
+  private Integer usersFetched;
+  private Integer usersCreated;
+  private Integer usersUpdated;
+  private Integer usersRemoved;
+  private Integer groupsFetched;
+  private Integer groupsCreated;
+  private Integer groupsUpdated;
+  private Integer groupsRemoved;
+  private Integer membershipsFetched;
+  private Integer membershipsCreated;
+  private Integer membershipsUpdated;
+
+  /**
+   * The specifications that define the sync event.
+   */
+  private List<LdapSyncSpecEntity> specs;
+
+
+  // ----- Constructors ------------------------------------------------------
+
+  /**
+   * Construct an LdapSyncEventEntity.
+   *
+   * @param id  the unique id
+   */
+  public LdapSyncEventEntity(long id) {
+    this.id = id;
+    this.status = Status.Pending;
+  }
+
+
+  // ----- LdapSyncEventEntity -----------------------------------------------
+
+  /**
+   * Get the id.
+   *
+   * @return the id
+   */
+  public long getId() {
+    return id;
+  }
+
+  /**
+   * Get the event status.
+   *
+   * @return the status
+   */
+  public Status getStatus() {
+    return status;
+  }
+
+  /**
+   * Set the event status.
+   *
+   * @param status  the status
+   */
+  public void setStatus(Status status) {
+    this.status = status;
+  }
+
+  /**
+   * Get the event status detail message.
+   *
+   * @return the event detail
+   */
+  public String getStatusDetail() {
+    return statusDetail;
+  }
+
+  /**
+   * Set the event status detail message.
+   *
+   * @param statusDetail  the event status detail message
+   */
+  public void setStatusDetail(String statusDetail) {
+    this.statusDetail = statusDetail;
+  }
+
+  /**
+   * Get the event specifications.
+   *
+   * @return the event specs
+   */
+  public List<LdapSyncSpecEntity> getSpecs() {
+    return specs;
+  }
+
+  /**
+   * Set the event specifications.
+   *
+   * @param specs  the event specifications
+   */
+  public void setSpecs(List<LdapSyncSpecEntity> specs) {
+    this.specs = specs;
+  }
+
+  /**
+   * Get the time that the sync started.
+   *
+   * @return the sync start time (millis)
+   */
+  public long getStartTime() {
+    return startTime;
+  }
+
+  /**
+   * Set the sync start time.
+   *
+   * @param startTime  the sync start time (millis)
+   */
+  public void setStartTime(long startTime) {
+    this.startTime = startTime;
+  }
+
+  /**
+   * Get the time the the sync event ended.
+   *
+   * @return  the end time (millis)
+   */
+  public long getEndTime() {
+    return endTime;
+  }
+
+  /**
+   * Set the sync end time.
+   *
+   * @param endTime  the end time (millis)
+   */
+  public void setEndTime(long endTime) {
+    this.endTime = endTime;
+  }
+
+  public Integer getUsersFetched() {
+    return usersFetched;
+  }
+
+  public void setUsersFetched(Integer usersFetched) {
+    this.usersFetched = usersFetched;
+  }
+
+  public Integer getUsersCreated() {
+    return usersCreated;
+  }
+
+  public void setUsersCreated(Integer usersCreated) {
+    this.usersCreated = usersCreated;
+  }
+
+  public Integer getUsersUpdated() {
+    return usersUpdated;
+  }
+
+  public void setUsersUpdated(Integer usersUpdated) {
+    this.usersUpdated = usersUpdated;
+  }
+
+  public Integer getUsersRemoved() {
+    return usersRemoved;
+  }
+
+  public void setUsersRemoved(Integer usersRemoved) {
+    this.usersRemoved = usersRemoved;
+  }
+
+  public Integer getGroupsFetched() {
+    return groupsFetched;
+  }
+
+  public void setGroupsFetched(Integer groupsFetched) {
+    this.groupsFetched = groupsFetched;
+  }
+
+  public Integer getGroupsCreated() {
+    return groupsCreated;
+  }
+
+  public void setGroupsCreated(Integer groupsCreated) {
+    this.groupsCreated = groupsCreated;
+  }
+
+  public Integer getGroupsUpdated() {
+    return groupsUpdated;
+  }
+
+  public void setGroupsUpdated(Integer groupsUpdated) {
+    this.groupsUpdated = groupsUpdated;
+  }
+
+  public Integer getGroupsRemoved() {
+    return groupsRemoved;
+  }
+
+  public void setGroupsRemoved(Integer groupsRemoved) {
+    this.groupsRemoved = groupsRemoved;
+  }
+
+  public Integer getMembershipsFetched() {
+    return membershipsFetched;
+  }
+
+  public void setMembershipsFetched(Integer membershipsFetched) {
+    this.membershipsFetched = membershipsFetched;
+  }
+
+  public Integer getMembershipsCreated() {
+    return membershipsCreated;
+  }
+
+  public void setMembershipsCreated(Integer membershipsCreated) {
+    this.membershipsCreated = membershipsCreated;
+  }
+
+  public Integer getMembershipsUpdated() {
+    return membershipsUpdated;
+  }
+
+  public void setMembershipsUpdated(Integer membershipsUpdated) {
+    this.membershipsUpdated = membershipsUpdated;
+  }
+
+
+  // ----- enum : Status -----------------------------------------------------
+
+  /**
+   * LDAP sync event status
+   */
+  public enum Status {
+    Pending,
+    Running,
+    Error,
+    Complete
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/1f7dbd5d/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/LdapSyncSpecEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/LdapSyncSpecEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/LdapSyncSpecEntity.java
new file mode 100644
index 0000000..027a4ce
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/LdapSyncSpecEntity.java
@@ -0,0 +1,143 @@
+/**
+ * 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.ambari.server.orm.entities;
+
+import java.util.List;
+
+/**
+ * LDAP sync specification entity.
+ */
+public class LdapSyncSpecEntity {
+
+  /**
+   * The principal type.
+   */
+  private PrincipalType principalType;
+
+  /**
+   * The sync type.
+   */
+  private SyncType syncType;
+
+  /**
+   * The list of principal names.
+   */
+  private List<String> principalNames;
+
+
+  // ----- Constructors ------------------------------------------------------
+
+  /**
+   * Construct an LdapSyncSpecEntity.
+   *
+   * @param principalType   the principal type
+   * @param syncType        the sync type
+   * @param principalNames  the list of principal names.
+   */
+  public LdapSyncSpecEntity(PrincipalType principalType, SyncType syncType, List<String> principalNames) {
+    this.principalType = principalType;
+    this.syncType = syncType;
+    this.principalNames = principalNames;
+
+    if (syncType == SyncType.Specific) {
+      if (principalNames.isEmpty()) {
+        throw new IllegalArgumentException("Missing principal names for " + syncType + " sync-type.");
+      }
+    } else {
+      if (!principalNames.isEmpty()) {
+        throw new IllegalArgumentException("Principal names should not be specified for " + syncType + " sync-type.");
+      }
+    }
+  }
+
+
+  // ----- LdapSyncSpecEntity ------------------------------------------------
+
+  /**
+   * Get the principal type.
+   *
+   * @return the principal type
+   */
+  public PrincipalType getPrincipalType() {
+    return principalType;
+  }
+
+  /**
+   * Get the sync type.
+   *
+   * @return the sync type
+   */
+  public SyncType getSyncType() {
+    return syncType;
+  }
+
+  /**
+   * Get a list of principal names.
+   *
+   * @return the list of principal names.
+   */
+  public List<String> getPrincipalNames() {
+    return principalNames;
+  }
+
+
+  // ----- enum : PrincipalType ----------------------------------------------
+
+  /**
+   * LDAP sync principal type.
+   */
+  public enum PrincipalType {
+    Users,
+    Groups;
+
+    /**
+     * Get the enum value for the given principal type name string, ignoring case.
+     *
+     * @param type  the principal type name
+     *
+     * @return the enum value for the given type name
+     */
+    public static PrincipalType valueOfIgnoreCase(String type) {
+      return valueOf(Character.toUpperCase(type.charAt(0)) + type.substring(1).toLowerCase());
+    }
+  }
+
+
+  // ----- enum : SyncType ---------------------------------------------------
+
+  /**
+   * LDAP sync type.
+   */
+  public enum SyncType {
+    All,
+    Existing,
+    Specific;
+
+    /**
+     * Get the enum value for the given sync type name string, ignoring case.
+     *
+     * @param type  the sync type name
+     *
+     * @return the enum value for the given type name
+     */
+    public static SyncType valueOfIgnoreCase(String type) {
+      return valueOf(Character.toUpperCase(type.charAt(0)) + type.substring(1).toLowerCase());
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/1f7dbd5d/ambari-server/src/test/java/org/apache/ambari/server/api/resources/LdapSyncEventResourceDefinitionTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/resources/LdapSyncEventResourceDefinitionTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/resources/LdapSyncEventResourceDefinitionTest.java
new file mode 100644
index 0000000..a0a3b60
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/api/resources/LdapSyncEventResourceDefinitionTest.java
@@ -0,0 +1,49 @@
+/**
+ * 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.ambari.server.api.resources;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Set;
+
+/**
+ * LdapSyncEventResourceDefinition tests.
+ */
+public class LdapSyncEventResourceDefinitionTest {
+  @Test
+  public void testGetPluralName() throws Exception {
+    LdapSyncEventResourceDefinition resourceDefinition = new LdapSyncEventResourceDefinition();
+    Assert.assertEquals("ldap_sync_events", resourceDefinition.getPluralName());
+  }
+
+  @Test
+  public void testGetSingularName() throws Exception {
+    LdapSyncEventResourceDefinition resourceDefinition = new LdapSyncEventResourceDefinition();
+    Assert.assertEquals("ldap_sync_event", resourceDefinition.getSingularName());
+  }
+
+  @Test
+  public void testGetSubResourceDefinitions() throws Exception {
+    LdapSyncEventResourceDefinition resourceDefinition = new LdapSyncEventResourceDefinition();
+    Set<SubResourceDefinition> subResourceDefinitions = resourceDefinition.getSubResourceDefinitions ();
+
+    Assert.assertEquals(0, subResourceDefinitions.size());
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/1f7dbd5d/ambari-server/src/test/java/org/apache/ambari/server/api/services/LdapSyncEventServiceTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/services/LdapSyncEventServiceTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/services/LdapSyncEventServiceTest.java
new file mode 100644
index 0000000..af458b2
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/api/services/LdapSyncEventServiceTest.java
@@ -0,0 +1,97 @@
+/**
+ * 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.ambari.server.api.services;
+
+import org.apache.ambari.server.api.resources.ResourceInstance;
+import org.apache.ambari.server.api.services.parsers.RequestBodyParser;
+import org.apache.ambari.server.api.services.serializers.ResultSerializer;
+import org.apache.ambari.server.controller.spi.Resource;
+
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.UriInfo;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * LdapSyncEventService tests.
+ */
+public class LdapSyncEventServiceTest extends BaseServiceTest {
+
+  public List<ServiceTestInvocation> getTestInvocations() throws Exception {
+    List<ServiceTestInvocation> listInvocations = new ArrayList<ServiceTestInvocation>();
+
+    //getEvent
+    LdapSyncEventService permissionService = new TestLdapSyncEventService();
+    Method m = permissionService.getClass().getMethod("getEvent", HttpHeaders.class, UriInfo.class, String.class);
+    Object[] args = new Object[] {getHttpHeaders(), getUriInfo(), "1"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.GET, permissionService, m, args, null));
+
+    //getEvents
+    permissionService = new TestLdapSyncEventService();
+    m = permissionService.getClass().getMethod("getEvents", HttpHeaders.class, UriInfo.class);
+    args = new Object[] {getHttpHeaders(), getUriInfo()};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.GET, permissionService, m, args, null));
+
+    //createEvent
+    permissionService = new TestLdapSyncEventService();
+    m = permissionService.getClass().getMethod("createEvent", String.class, HttpHeaders.class, UriInfo.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo()};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.POST, permissionService, m, args, "body"));
+
+    //deleteEvent
+    permissionService = new TestLdapSyncEventService();
+    m = permissionService.getClass().getMethod("deleteEvent", HttpHeaders.class, UriInfo.class, String.class);
+    args = new Object[] {getHttpHeaders(), getUriInfo(), "1"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.DELETE, permissionService, m, args, null));
+
+    return listInvocations;
+  }
+
+
+  private class TestLdapSyncEventService extends LdapSyncEventService {
+
+    private TestLdapSyncEventService() {
+      super();
+    }
+
+    @Override
+    protected ResourceInstance createResource(Resource.Type type, Map<Resource.Type, String> mapIds) {
+      return getTestResource();
+    }
+
+    @Override
+    RequestFactory getRequestFactory() {
+      return getTestRequestFactory();
+    }
+
+    @Override
+    protected RequestBodyParser getBodyParser() {
+      return getTestBodyParser();
+    }
+
+    @Override
+    protected ResultSerializer getResultSerializer() {
+      return getTestResultSerializer();
+    }
+  }
+  
+  
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/1f7dbd5d/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/LdapSyncEventResourceProviderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/LdapSyncEventResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/LdapSyncEventResourceProviderTest.java
new file mode 100644
index 0000000..92bd7a4
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/LdapSyncEventResourceProviderTest.java
@@ -0,0 +1,114 @@
+/**
+ * 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.ambari.server.controller.internal;
+
+import org.apache.ambari.server.controller.spi.Request;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import static org.easymock.EasyMock.createNiceMock;
+
+/**
+ * LdapSyncEventResourceProvider tests.
+ */
+public class LdapSyncEventResourceProviderTest {
+  @Test
+  public void testCreateResources() throws Exception {
+    LdapSyncEventResourceProvider provider = new TestLdapSyncEventResourceProvider();
+
+    Set<Map<String, Object>> properties = new HashSet<Map<String, Object>>();
+    Map<String, Object> propertyMap = new HashMap<String, Object>();
+
+    Set<Map<String, String>> specs = new HashSet<Map<String, String>>();
+
+    propertyMap.put(LdapSyncEventResourceProvider.EVENT_SPECS_PROPERTY_ID, specs);
+    properties.add(propertyMap);
+
+    provider.createResources(PropertyHelper.getCreateRequest(properties, null));
+  }
+
+  @Test
+  public void testGetResources() throws Exception {
+    LdapSyncEventResourceProvider provider = new TestLdapSyncEventResourceProvider();
+
+    Set<Map<String, Object>> properties = new HashSet<Map<String, Object>>();
+    Map<String, Object> propertyMap = new HashMap<String, Object>();
+
+    Set<Map<String, String>> specs = new HashSet<Map<String, String>>();
+
+    propertyMap.put(LdapSyncEventResourceProvider.EVENT_SPECS_PROPERTY_ID, specs);
+    properties.add(propertyMap);
+
+    provider.createResources(PropertyHelper.getCreateRequest(properties, null));
+
+    Set<Resource> resources = provider.getResources(PropertyHelper.getReadRequest(), null);
+    Assert.assertEquals(1, resources.size());
+  }
+
+  @Test
+  public void testUpdateResources() throws Exception {
+    LdapSyncEventResourceProvider provider = new TestLdapSyncEventResourceProvider();
+
+    Request request = createNiceMock(Request.class);
+
+    try {
+      provider.updateResources(request, null);
+      Assert.fail("expected UnsupportedOperationException");
+    } catch (UnsupportedOperationException e) {
+      // expected
+    }
+  }
+
+  @Test
+  public void testDeleteResources() throws Exception {
+    LdapSyncEventResourceProvider provider = new TestLdapSyncEventResourceProvider();
+
+    Set<Map<String, Object>> properties = new HashSet<Map<String, Object>>();
+    Map<String, Object> propertyMap = new HashMap<String, Object>();
+
+    Set<Map<String, String>> specs = new HashSet<Map<String, String>>();
+
+    propertyMap.put(LdapSyncEventResourceProvider.EVENT_SPECS_PROPERTY_ID, specs);
+    properties.add(propertyMap);
+
+    provider.createResources(PropertyHelper.getCreateRequest(properties, null));
+
+    Set<Resource> resources = provider.getResources(PropertyHelper.getReadRequest(), null);
+    Assert.assertEquals(1, resources.size());
+
+    provider.deleteResources(null);
+
+    resources = provider.getResources(PropertyHelper.getReadRequest(), null);
+    Assert.assertEquals(0, resources.size());
+  }
+
+
+  protected static class TestLdapSyncEventResourceProvider extends LdapSyncEventResourceProvider {
+    @Override
+    protected void ensureEventProcessor() {
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/1f7dbd5d/ambari-server/src/test/java/org/apache/ambari/server/orm/entities/LdapSyncEventEntityTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/orm/entities/LdapSyncEventEntityTest.java b/ambari-server/src/test/java/org/apache/ambari/server/orm/entities/LdapSyncEventEntityTest.java
new file mode 100644
index 0000000..66f9d6d
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/orm/entities/LdapSyncEventEntityTest.java
@@ -0,0 +1,166 @@
+/**
+ * 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.ambari.server.orm.entities;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * LdapSyncEventEntity tests.
+ */
+public class LdapSyncEventEntityTest {
+  @Test
+  public void testGetId() throws Exception {
+    LdapSyncEventEntity event = new LdapSyncEventEntity(1L);
+    Assert.assertEquals(1L, event.getId());
+  }
+
+  @Test
+  public void testSetGetStatus() throws Exception {
+    LdapSyncEventEntity event = new LdapSyncEventEntity(1L);
+    Assert.assertEquals(LdapSyncEventEntity.Status.Pending, event.getStatus());
+
+    event.setStatus(LdapSyncEventEntity.Status.Running);
+    Assert.assertEquals(LdapSyncEventEntity.Status.Running, event.getStatus());
+
+    event.setStatus(LdapSyncEventEntity.Status.Complete);
+    Assert.assertEquals(LdapSyncEventEntity.Status.Complete, event.getStatus());
+
+    event.setStatus(LdapSyncEventEntity.Status.Error);
+    Assert.assertEquals(LdapSyncEventEntity.Status.Error, event.getStatus());
+
+    event.setStatus(LdapSyncEventEntity.Status.Pending);
+    Assert.assertEquals(LdapSyncEventEntity.Status.Pending, event.getStatus());
+  }
+
+  @Test
+  public void testSetGetStatusDetail() throws Exception {
+    LdapSyncEventEntity event = new LdapSyncEventEntity(1L);
+    event.setStatusDetail("some detail");
+    Assert.assertEquals("some detail", event.getStatusDetail());
+  }
+
+  @Test
+  public void testSetGetSpecs() throws Exception {
+    LdapSyncEventEntity event = new LdapSyncEventEntity(1L);
+    LdapSyncSpecEntity spec = new LdapSyncSpecEntity(LdapSyncSpecEntity.PrincipalType.Groups,
+        LdapSyncSpecEntity.SyncType.All, Collections.<String>emptyList());
+
+    event.setSpecs(Collections.singletonList(spec));
+
+    List<LdapSyncSpecEntity> specs = event.getSpecs();
+    Assert.assertEquals(1, specs.size());
+
+    Assert.assertEquals(spec, specs.get(0));
+  }
+
+  @Test
+  public void testSetGetStartTime() throws Exception {
+    LdapSyncEventEntity event = new LdapSyncEventEntity(1L);
+    event.setStartTime(10001000L);
+    Assert.assertEquals(10001000L, event.getStartTime());
+  }
+
+  @Test
+  public void testSetGetEndTime() throws Exception {
+    LdapSyncEventEntity event = new LdapSyncEventEntity(1L);
+    event.setEndTime(90009000L);
+    Assert.assertEquals(90009000L, event.getEndTime());
+  }
+
+  @Test
+  public void testSetGetUsersFetched() throws Exception {
+    LdapSyncEventEntity event = new LdapSyncEventEntity(1L);
+    event.setUsersFetched(99);
+    Assert.assertEquals(Integer.valueOf(99), event.getUsersFetched());
+  }
+
+  @Test
+  public void testSetGetUsersCreated() throws Exception {
+    LdapSyncEventEntity event = new LdapSyncEventEntity(1L);
+    event.setUsersCreated(98);
+    Assert.assertEquals(Integer.valueOf(98), event.getUsersCreated());
+  }
+
+  @Test
+  public void testSetGetUsersUpdated() throws Exception {
+    LdapSyncEventEntity event = new LdapSyncEventEntity(1L);
+    event.setUsersUpdated(97);
+    Assert.assertEquals(Integer.valueOf(97), event.getUsersUpdated());
+  }
+
+  @Test
+  public void testSetGetUsersRemoved() throws Exception {
+    LdapSyncEventEntity event = new LdapSyncEventEntity(1L);
+    event.setUsersRemoved(96);
+    Assert.assertEquals(Integer.valueOf(96), event.getUsersRemoved());
+  }
+
+  @Test
+  public void testSetGetGroupsFetched() throws Exception {
+    LdapSyncEventEntity event = new LdapSyncEventEntity(1L);
+    event.setGroupsFetched(95);
+    Assert.assertEquals(Integer.valueOf(95), event.getGroupsFetched());
+  }
+
+  @Test
+  public void testSetGetGroupsCreated() throws Exception {
+    LdapSyncEventEntity event = new LdapSyncEventEntity(1L);
+    event.setGroupsCreated(94);
+    Assert.assertEquals(Integer.valueOf(94), event.getGroupsCreated());
+  }
+
+  @Test
+  public void testSetGetGroupsUpdated() throws Exception {
+    LdapSyncEventEntity event = new LdapSyncEventEntity(1L);
+    event.setGroupsUpdated(93);
+    Assert.assertEquals(Integer.valueOf(93), event.getGroupsUpdated());
+  }
+
+  @Test
+  public void testSetGetGroupsRemoved() throws Exception {
+    LdapSyncEventEntity event = new LdapSyncEventEntity(1L);
+    event.setGroupsRemoved(92);
+    Assert.assertEquals(Integer.valueOf(92), event.getGroupsRemoved());
+  }
+
+  @Test
+  public void testSetGetMembershipsFetched() throws Exception {
+    LdapSyncEventEntity event = new LdapSyncEventEntity(1L);
+    event.setMembershipsFetched(91);
+    Assert.assertEquals(Integer.valueOf(91), event.getMembershipsFetched());
+  }
+
+  @Test
+  public void testSetGetMembershipsCreated() throws Exception {
+    LdapSyncEventEntity event = new LdapSyncEventEntity(1L);
+    event.setMembershipsCreated(90);
+    Assert.assertEquals(Integer.valueOf(90), event.getMembershipsCreated());
+  }
+
+  @Test
+  public void testSetGetMembershipsUpdated() throws Exception {
+    LdapSyncEventEntity event = new LdapSyncEventEntity(1L);
+    event.setMembershipsUpdated(99);
+    Assert.assertEquals(Integer.valueOf(99), event.getMembershipsUpdated());
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/1f7dbd5d/ambari-server/src/test/java/org/apache/ambari/server/orm/entities/LdapSyncSpecEntityTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/orm/entities/LdapSyncSpecEntityTest.java b/ambari-server/src/test/java/org/apache/ambari/server/orm/entities/LdapSyncSpecEntityTest.java
new file mode 100644
index 0000000..468281f
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/orm/entities/LdapSyncSpecEntityTest.java
@@ -0,0 +1,95 @@
+/**
+ * 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.ambari.server.orm.entities;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * LdapSyncSpecEntity tests.
+ */
+public class LdapSyncSpecEntityTest {
+  @Test
+  public void testGetPrincipalType() throws Exception {
+    LdapSyncSpecEntity entity = new LdapSyncSpecEntity(LdapSyncSpecEntity.PrincipalType.Users,
+        LdapSyncSpecEntity.SyncType.All, Collections.<String>emptyList());
+    Assert.assertEquals(LdapSyncSpecEntity.PrincipalType.Users, entity.getPrincipalType());
+
+    entity = new LdapSyncSpecEntity(LdapSyncSpecEntity.PrincipalType.Groups,
+        LdapSyncSpecEntity.SyncType.All, Collections.<String>emptyList());
+    Assert.assertEquals(LdapSyncSpecEntity.PrincipalType.Groups, entity.getPrincipalType());
+  }
+
+  @Test
+  public void testGetSyncType() throws Exception {
+    LdapSyncSpecEntity entity = new LdapSyncSpecEntity(LdapSyncSpecEntity.PrincipalType.Users,
+        LdapSyncSpecEntity.SyncType.All, Collections.<String>emptyList());
+    Assert.assertEquals(LdapSyncSpecEntity.SyncType.All, entity.getSyncType());
+
+    entity = new LdapSyncSpecEntity(LdapSyncSpecEntity.PrincipalType.Users,
+        LdapSyncSpecEntity.SyncType.Existing, Collections.<String>emptyList());
+    Assert.assertEquals(LdapSyncSpecEntity.SyncType.Existing, entity.getSyncType());
+  }
+
+  @Test
+  public void testGetPrincipalNames() throws Exception {
+    List<String> names = new LinkedList<String>();
+    names.add("joe");
+    names.add("fred");
+
+    LdapSyncSpecEntity entity = new LdapSyncSpecEntity(LdapSyncSpecEntity.PrincipalType.Users,
+        LdapSyncSpecEntity.SyncType.Specific, names);
+    Assert.assertEquals(names, entity.getPrincipalNames());
+  }
+
+  @Test
+  public void testIllegalConstruction() throws Exception {
+    try {
+      new LdapSyncSpecEntity(LdapSyncSpecEntity.PrincipalType.Users,
+          LdapSyncSpecEntity.SyncType.Specific, Collections.<String>emptyList());
+      Assert.fail("expected IllegalArgumentException");
+    } catch (IllegalArgumentException e) {
+      // expected
+    }
+
+    List<String> names = new LinkedList<String>();
+    names.add("joe");
+    names.add("fred");
+
+    try {
+      new LdapSyncSpecEntity(LdapSyncSpecEntity.PrincipalType.Users,
+          LdapSyncSpecEntity.SyncType.All, names);
+      Assert.fail("expected IllegalArgumentException");
+    } catch (IllegalArgumentException e) {
+      // expected
+    }
+
+    try {
+      new LdapSyncSpecEntity(LdapSyncSpecEntity.PrincipalType.Users,
+          LdapSyncSpecEntity.SyncType.Existing, names);
+      Assert.fail("expected IllegalArgumentException");
+    } catch (IllegalArgumentException e) {
+      // expected
+    }
+  }
+}