http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/6d19e129/odf/odf-web/src/main/java/org/apache/atlas/odf/admin/rest/resources/AnalysesResource.java
----------------------------------------------------------------------
diff --git 
a/odf/odf-web/src/main/java/org/apache/atlas/odf/admin/rest/resources/AnalysesResource.java
 
b/odf/odf-web/src/main/java/org/apache/atlas/odf/admin/rest/resources/AnalysesResource.java
new file mode 100755
index 0000000..a3bc3b2
--- /dev/null
+++ 
b/odf/odf-web/src/main/java/org/apache/atlas/odf/admin/rest/resources/AnalysesResource.java
@@ -0,0 +1,156 @@
+/**
+ *
+ * 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.atlas.odf.admin.rest.resources;
+
+import java.util.logging.Logger;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.apache.atlas.odf.api.analysis.AnalysisCancelResult;
+import org.apache.atlas.odf.api.analysis.AnalysisRequest;
+import org.apache.atlas.odf.api.analysis.AnalysisRequestStatus;
+import org.apache.atlas.odf.api.analysis.AnalysisResponse;
+import org.apache.atlas.odf.json.JSONUtils;
+import org.apache.wink.json4j.JSONException;
+
+import org.apache.atlas.odf.admin.rest.RestUtils;
+import org.apache.atlas.odf.api.analysis.AnalysisRequestSummary;
+import org.apache.atlas.odf.api.analysis.AnalysisRequestTrackers;
+import org.apache.atlas.odf.api.ODFFactory;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+
+@Path("/analyses")
+@Api(value = "/analyses", description = "Create and view analysis requests", 
produces = MediaType.APPLICATION_JSON)
+public class AnalysesResource {
+       private Logger logger = 
Logger.getLogger(AnalysesResource.class.getName());
+
+       @GET
+       @Path("/stats")
+       @Produces(MediaType.APPLICATION_JSON)
+       @ApiOperation(value = "Get analysis request statistics", httpMethod = 
"GET", notes = "Return number of successfull and failing analysis requests", 
response = AnalysisRequestSummary.class)
+       @ApiResponses(value = {
+                       @ApiResponse(code = 200, message = "OK"),
+                       @ApiResponse(code = 500, message = "Internal server 
error")
+       })
+       public Response getStats() {
+               try {
+                       return Response.ok(JSONUtils.toJSON(new 
ODFFactory().create().getAnalysisManager().getAnalysisStats())).build();
+               } catch (JSONException e) {
+                       e.printStackTrace();
+                       logger.info("Parse exception " + e);
+                       return RestUtils.createErrorResponse(e);
+               }
+       }
+
+       @GET
+       @Produces(MediaType.APPLICATION_JSON)
+       @ApiOperation(value = "Get list of analysis requests", httpMethod = 
"GET", notes = "Retrieve list of recent analysis requests (from latest to 
oldest)", responseContainer="List", response = AnalysisRequestTrackers.class)
+       @ApiResponses(value = {
+                       @ApiResponse(code = 200, message = "OK"),
+                       @ApiResponse(code = 500, message = "Internal server 
error")
+       })
+       public Response getAnalysisRequests(
+                       @ApiParam(value = "Starting offset (use 0 to start with 
the latest request).", required = false)
+                       @DefaultValue("0") @QueryParam("offset") int offset,
+                       @ApiParam(value = "Maximum number of analysis requests 
to be returned (use -1 to retrieve all requests).", required = false)
+                       @DefaultValue("10") @QueryParam("limit") int limit) {
+               try {
+                       String result = JSONUtils.toJSON(new 
ODFFactory().create().getAnalysisManager().getAnalysisRequests(offset, limit));
+                       return Response.ok(result).build();
+               } catch (Exception exc) {
+                       throw new RuntimeException(exc);
+               }
+       }
+
+       @GET
+       @Path("/{requestId}")
+       @Produces(MediaType.APPLICATION_JSON)
+       @ApiOperation(value = "Get analysis request status", httpMethod = 
"GET", notes = "Show status of a specific analysis request", response = 
AnalysisRequestStatus.class)
+       @ApiResponses(value = {
+                       @ApiResponse(code = 200, message = "OK"),
+                       @ApiResponse(code = 400, message = "Bad Request"),
+                       @ApiResponse(code = 500, message = "Internal server 
error")
+       })
+       public Response getAnalysisStatus(
+                       @ApiParam(value = "ID of the analysis request", 
required = true)
+                       @PathParam("requestId") String requestId) {
+               logger.entering(AnalysesResource.class.getName(), 
"getAnalysisStatus");
+               AnalysisRequestStatus analysisRequestStatus = new 
ODFFactory().create().getAnalysisManager().getAnalysisRequestStatus(requestId);
+               try {
+                       return 
Response.ok(JSONUtils.toJSON(analysisRequestStatus)).build();
+               } catch (JSONException e) {
+                       e.printStackTrace();
+                       logger.info("Parse exception " + e);
+                       return RestUtils.createErrorResponse(e);
+               }
+       }
+
+       @POST
+       @Produces(MediaType.APPLICATION_JSON)
+       @Consumes(MediaType.APPLICATION_JSON)
+       @ApiOperation(value = "Run analysis", httpMethod = "POST", notes = 
"Create and run new analysis request", response = AnalysisResponse.class)
+       @ApiResponses(value = {
+                       @ApiResponse(code = 200, message = "OK"),
+                       @ApiResponse(code = 400, message = "Bad Request"),
+                       @ApiResponse(code = 500, message = "Internal server 
error")
+       })
+       public Response startAnalysis(@ApiParam(value = "Analysis request to be 
started", required = true) AnalysisRequest request) {
+               logger.entering(AnalysesResource.class.getName(), 
"startAnalysis");
+               try {
+                       AnalysisResponse analysisResponse = new 
ODFFactory().create().getAnalysisManager().runAnalysis(request);
+                       return 
Response.ok(JSONUtils.toJSON(analysisResponse)).build();
+               } catch (JSONException e) {
+                       e.printStackTrace();
+                       logger.info("Parse exception " + e);
+                       return RestUtils.createErrorResponse(e);
+               }
+       }
+
+       @POST
+       @Path("/{requestId}/cancel")
+       @Produces(MediaType.APPLICATION_JSON)
+       @ApiOperation(value = "Cancel analysis request", httpMethod = "POST", 
notes = "Cancel a queued analysis request that has not been started yet", 
response = Response.class)
+       @ApiResponses(value = {
+                       @ApiResponse(code = 200, message = "OK"),
+                       @ApiResponse(code = 400, message = "Bad Request - The 
request with the provided id could not be found"),
+                       @ApiResponse(code = 403, message = "Forbidden - The 
status of the analysis request does not allow for cancellation")
+       })
+       public Response cancelAnalysisRequest(@ApiParam(value = "ID of the 
analysis request", required = true) @PathParam("requestId") String requestId) {
+               logger.entering(AnalysesResource.class.getName(), 
"cancelAnalysisRequest");
+               AnalysisCancelResult result = new 
ODFFactory().create().getAnalysisManager().cancelAnalysisRequest(requestId);
+               if (result.getState() == AnalysisCancelResult.State.NOT_FOUND) {
+                       return Response.status(Status.BAD_REQUEST).build();
+               } else if (result.getState() == 
AnalysisCancelResult.State.INVALID_STATE) {
+                       return Response.status(Status.FORBIDDEN).build();
+               }
+               return Response.ok().build();
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/6d19e129/odf/odf-web/src/main/java/org/apache/atlas/odf/admin/rest/resources/AnnotationsResource.java
----------------------------------------------------------------------
diff --git 
a/odf/odf-web/src/main/java/org/apache/atlas/odf/admin/rest/resources/AnnotationsResource.java
 
b/odf/odf-web/src/main/java/org/apache/atlas/odf/admin/rest/resources/AnnotationsResource.java
new file mode 100755
index 0000000..704b004
--- /dev/null
+++ 
b/odf/odf-web/src/main/java/org/apache/atlas/odf/admin/rest/resources/AnnotationsResource.java
@@ -0,0 +1,130 @@
+/**
+ *
+ * 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.atlas.odf.admin.rest.resources;
+
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.apache.atlas.odf.admin.rest.RestUtils;
+import org.apache.atlas.odf.api.metadata.MetaDataObjectReference;
+import org.apache.atlas.odf.api.metadata.models.Annotation;
+import org.apache.atlas.odf.api.ODFFactory;
+import org.apache.atlas.odf.api.annotation.AnnotationStore;
+import org.apache.atlas.odf.api.annotation.AnnotationStoreUtils;
+import org.apache.atlas.odf.api.annotation.Annotations;
+import org.apache.atlas.odf.json.JSONUtils;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+
+@Path("/annotations")
+@Api(value = "/annotations", description = "Create and query ODF annotations", 
produces = MediaType.APPLICATION_JSON)
+public class AnnotationsResource {
+
+       Logger logger = Logger.getLogger(AnnotationsResource.class.getName());
+
+       @GET
+       @Produces(MediaType.APPLICATION_JSON)
+       @ApiOperation(value = "Retrieve annotations", httpMethod = "GET", notes 
= "Retrieve annotations for an asset and/or for a specific analysis request.", 
response = Annotations.class)
+       @ApiResponses(value = { @ApiResponse(code = 200, message = "OK"), 
@ApiResponse(code = 500, message = "Internal server error") })
+       public Response retrieveAnnotationsForAsset(@ApiParam(value = 
"Reference ID of the asset", required = false) @QueryParam("assetReference") 
String assetReference,
+                       @ApiParam(value = "Analysis request ID", required = 
false) @QueryParam("analysisRequestId") String analysisRequestId) {
+               try {
+                       MetaDataObjectReference ref = null;
+                       if (assetReference != null) {
+                               ref = new MetaDataObjectReference();
+                               String repoId = new 
ODFFactory().create().getMetadataStore().getRepositoryId();
+                               ref.setRepositoryId(repoId);
+                               ref.setId(assetReference);
+                       }
+                       AnnotationStore as = new 
ODFFactory().create().getAnnotationStore();
+                       List<Annotation> annots = as.getAnnotations(ref, 
analysisRequestId);
+                       Annotations result = new Annotations();
+                       result.setAnnotations(annots);
+                       return Response.ok(JSONUtils.toJSON(result)).build();
+               } catch (Exception exc) {
+                       logger.log(Level.WARNING, "An exception occurred while 
retrieving annotations", exc);
+                       return RestUtils.createErrorResponse(exc);
+               }
+       }
+
+
+       @GET
+       @Path("/objects/{objectReference}")
+       @Produces(MediaType.APPLICATION_JSON)
+       @ApiOperation(value = "Retrieve annotation", httpMethod = "GET", notes 
= "Retrieve annotation by Id.", response = Annotation.class)
+       @ApiResponses(value = { @ApiResponse(code = 200, message = "OK"), 
@ApiResponse(code = 500, message = "Internal server error") })
+       public Response retrieveAnnotation(@ApiParam(value = "Reference ID of 
the annotation", required = true) @PathParam("objectReference") String 
objectReference) {
+               try {
+                       MetaDataObjectReference ref = new 
MetaDataObjectReference();
+                       AnnotationStore as = new 
ODFFactory().create().getAnnotationStore();
+                       ref.setRepositoryId(as.getRepositoryId());
+                       ref.setId(objectReference);
+                       Annotation annot = as.retrieveAnnotation(ref);
+                       return Response.ok(JSONUtils.toJSON(annot)).build();
+               } catch (Exception exc) {
+                       logger.log(Level.WARNING, "An exception occurred while 
retrieving annotation", exc);
+                       return RestUtils.createErrorResponse(exc);
+               }
+       }
+
+
+       // no swagger documentation as this will be replaced by "annotation 
propagation"
+       @GET
+       @Path("/newestAnnotations/{assetReference}")
+       @Produces(MediaType.APPLICATION_JSON)
+       public Response 
retrieveMostRecentAnnotations(@PathParam("assetReference") String 
assetReference) {
+               try {
+                       MetaDataObjectReference ref = 
JSONUtils.fromJSON(assetReference, MetaDataObjectReference.class);
+                       AnnotationStore as = new 
ODFFactory().create().getAnnotationStore();
+                       List<Annotation> annotations = 
AnnotationStoreUtils.getMostRecentAnnotationsByType(as, ref);
+                       String result = JSONUtils.toJSON(annotations);
+                       return Response.ok(result).build();
+               } catch (Exception e) {
+                       logger.log(Level.WARNING, "An exception occurred while 
retrieving most recent annotations", e);
+                       return RestUtils.createErrorResponse(e);
+               }
+       }
+
+       @POST
+       @ApiOperation(value = "Create annotation", httpMethod = "POST", notes = 
"Create new annotation object", response = MetaDataObjectReference.class)
+       @ApiResponses(value = { @ApiResponse(code = 200, message = "OK"), 
@ApiResponse(code = 400, message = "Bad Request"), @ApiResponse(code = 500, 
message = "Internal server error") })
+       public Response createAnnotation(@ApiParam(value = "Analysis request to 
be started", required = true) String annotString) {
+               try {
+                       Annotation annot = JSONUtils.fromJSON(annotString, 
Annotation.class);
+                       AnnotationStore as = new 
ODFFactory().create().getAnnotationStore();
+                       MetaDataObjectReference annotRef = as.store(annot);
+                       return 
Response.status(Status.CREATED).entity(JSONUtils.toJSON(annotRef)).build();
+               } catch (Exception exc) {
+                       logger.log(Level.WARNING, "An exception occurred while 
storing an annotation", exc);
+                       return RestUtils.createErrorResponse(exc);
+               }
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/6d19e129/odf/odf-web/src/main/java/org/apache/atlas/odf/admin/rest/resources/DiscoveryServicesResource.java
----------------------------------------------------------------------
diff --git 
a/odf/odf-web/src/main/java/org/apache/atlas/odf/admin/rest/resources/DiscoveryServicesResource.java
 
b/odf/odf-web/src/main/java/org/apache/atlas/odf/admin/rest/resources/DiscoveryServicesResource.java
new file mode 100755
index 0000000..bd01e60
--- /dev/null
+++ 
b/odf/odf-web/src/main/java/org/apache/atlas/odf/admin/rest/resources/DiscoveryServicesResource.java
@@ -0,0 +1,341 @@
+/**
+ * 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.atlas.odf.admin.rest.resources;
+
+import java.io.InputStream;
+import java.util.List;
+import java.util.logging.Logger;
+
+import javax.ws.rs.Consumes;
+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.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.apache.atlas.odf.api.settings.validation.ValidationException;
+import org.apache.wink.json4j.JSONException;
+
+import org.apache.atlas.odf.admin.rest.RestUtils;
+import org.apache.atlas.odf.api.ODFFactory;
+import org.apache.atlas.odf.api.discoveryservice.DiscoveryServiceManager;
+import org.apache.atlas.odf.api.discoveryservice.DiscoveryServiceProperties;
+import 
org.apache.atlas.odf.api.discoveryservice.DiscoveryServiceRuntimeStatistics;
+import org.apache.atlas.odf.api.discoveryservice.DiscoveryServiceStatus;
+import org.apache.atlas.odf.api.discoveryservice.ServiceNotFoundException;
+import org.apache.atlas.odf.api.discoveryservice.ServiceStatusCount;
+import org.apache.atlas.odf.json.JSONUtils;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+
+@Path("/services")
+@Api(value = "/services", description = "Manage ODF services", produces = 
MediaType.APPLICATION_JSON)
+public class DiscoveryServicesResource {
+       private Logger logger = 
Logger.getLogger(DiscoveryServicesResource.class.getName());
+
+       @GET
+       @Produces(MediaType.APPLICATION_JSON)
+       @ApiOperation(value = "Get list of discovery services", httpMethod = 
"GET", notes = "Retrieve list of all discovery services registered in ODF", 
responseContainer="List", response = DiscoveryServiceProperties.class)
+       @ApiResponses(value = {
+                       @ApiResponse(code = 200, message = "OK"),
+                       @ApiResponse(code = 500, message = "Internal server 
error")
+       })
+       public Response getDiscoveryServices() {
+               logger.entering(DiscoveryServicesResource.class.getName(), 
"getServices");
+               DiscoveryServiceManager dsAdmin = new 
ODFFactory().create().getDiscoveryServiceManager();
+               Response response;
+               List<DiscoveryServiceProperties> dsProperties = 
dsAdmin.getDiscoveryServicesProperties();
+               try {
+                       String json = JSONUtils.toJSON(dsProperties);
+                       response = Response.ok(json).build();
+               } catch (JSONException e) {
+                       e.printStackTrace();
+                       logger.info("Parse exception " + e);
+                       response = RestUtils.createErrorResponse(e);
+               }
+               return response;
+       }
+
+       @GET
+       @Produces(MediaType.APPLICATION_JSON)
+       @Path("/status")
+       @ApiOperation(value = "Get status of discovery services", httpMethod = 
"GET", notes = "Retrieve status overview of all discovery services registered 
in ODF", responseContainer="List", response = ServiceStatusCount.class)
+       @ApiResponses(value = {
+                       @ApiResponse(code = 200, message = "OK"),
+                       @ApiResponse(code = 404, message = "Not found"),
+                       @ApiResponse(code = 500, message = "Internal server 
error")
+       })
+       public Response getAllServicesStatus() {
+               logger.entering(DiscoveryServicesResource.class.getName(), 
"getAllServicesStatus");
+               List<ServiceStatusCount> servicesStatus = new 
ODFFactory().create().getDiscoveryServiceManager().getDiscoveryServiceStatusOverview();
+               if (servicesStatus == null) {
+                       return Response.status(Status.NOT_FOUND).build();
+               }
+               String json;
+               try {
+                       json = JSONUtils.toJSON(servicesStatus);
+               } catch (JSONException e) {
+                       throw new RuntimeException(e);
+               }
+               return Response.ok(json).build();
+       }
+
+       @GET
+       @Produces(MediaType.APPLICATION_JSON)
+       @Path("/{serviceId}/status")
+       @ApiOperation(value = "Get discovery service status", httpMethod = 
"GET", notes = "Retrieve status of a discovery service that is registered in 
ODF", response = Response.class)
+       @ApiResponses(value = {
+                       @ApiResponse(code = 200, message = "OK"),
+                       @ApiResponse(code = 404, message = "Not found"),
+                       @ApiResponse(code = 500, message = "Internal server 
error")
+       })
+       public Response getDiscoveryServiceStatus(
+                       @ApiParam(value = "Discovery service ID", required = 
true)
+                       @PathParam("serviceId") String serviceId) {
+               logger.entering(DiscoveryServicesResource.class.getName(), 
"getDiscoveryServiceStatus");
+               DiscoveryServiceManager dsAdmin = new 
ODFFactory().create().getDiscoveryServiceManager();
+               Response response;
+               try {
+                       DiscoveryServiceStatus dsStatus = 
dsAdmin.getDiscoveryServiceStatus(serviceId);
+                       if (dsStatus == null) {
+                               response = 
Response.status(Status.NOT_FOUND).build();
+                       }
+                       else {
+                               try {
+                                       String json = 
JSONUtils.toJSON(dsStatus);
+                                       response = Response.ok(json).build();
+                               } catch (JSONException e) {
+                                       e.printStackTrace();
+                                       logger.info("Parse exception " + e);
+                                       response = 
RestUtils.createErrorResponse(e);
+                               }
+                       }
+               }
+               catch (ServiceNotFoundException snfe) {
+                       response = 
Response.status(Status.NOT_FOUND).entity(snfe.getMessage()).build();
+               }
+               return response;
+       }
+
+       @GET
+       @Produces(MediaType.APPLICATION_JSON)
+       @Path("/{serviceId}/runtimeStats")
+       @ApiOperation(value = "Get runtime statistics of a discovery service", 
httpMethod = "GET", notes = "Retrieve the runtime statistics of a discovery 
service that is registered in ODF.", response = Response.class)
+       @ApiResponses(value = {
+                       @ApiResponse(code = 200, message = "OK"),
+                       @ApiResponse(code = 404, message = "Not found"),
+                       @ApiResponse(code = 500, message = "Internal server 
error")
+       })
+       public Response getDiscoveryServiceRuntimeStats(
+                       @ApiParam(value = "Discovery service ID", required = 
true)
+                       @PathParam("serviceId") String serviceId) {
+               logger.entering(DiscoveryServicesResource.class.getName(), 
"getDiscoveryServiceRuntimeStats");
+               DiscoveryServiceManager dsAdmin = new 
ODFFactory().create().getDiscoveryServiceManager();
+               Response response;
+               try {
+                       DiscoveryServiceRuntimeStatistics dsRuntimeStats = 
dsAdmin.getDiscoveryServiceRuntimeStatistics(serviceId);
+                       String json = JSONUtils.toJSON(dsRuntimeStats);
+                       response = Response.ok(json).build();
+               }
+               catch (JSONException e) {
+                       e.printStackTrace();
+                       logger.info("Parse exception " + e);
+                       response = RestUtils.createErrorResponse(e);
+               }
+               catch (ServiceNotFoundException snfe) {
+                       response = 
Response.status(Status.NOT_FOUND).entity(snfe.getMessage()).build();
+               }
+               return response;
+       }
+
+       @DELETE
+       @Path("/{serviceId}/runtimeStats")
+       @ApiOperation(value = "Delete runtime statistics of a discovery 
service", httpMethod = "DELETE", notes = "Delete the runtime statistics of a 
discovery service that is registered in ODF.", response = Response.class)
+       @ApiResponses(value = {
+                       @ApiResponse(code = 200, message = "OK"),
+                       @ApiResponse(code = 404, message = "Not found"),
+                       @ApiResponse(code = 500, message = "Internal server 
error")
+       })
+       public Response deleteDiscoveryServiceRuntimeStats(
+                       @ApiParam(value = "Discovery service ID", required = 
true)
+                       @PathParam("serviceId") String serviceId) {
+               logger.entering(DiscoveryServicesResource.class.getName(), 
"deleteDiscoveryServiceRuntimeStats");
+               DiscoveryServiceManager dsAdmin = new 
ODFFactory().create().getDiscoveryServiceManager();
+               Response response;
+               try {
+                       
dsAdmin.deleteDiscoveryServiceRuntimeStatistics(serviceId);
+                       response = Response.ok().build();
+               }
+               catch (ServiceNotFoundException snfe) {
+                       response = 
Response.status(Status.NOT_FOUND).entity(snfe.getMessage()).build();
+               }
+               return response;
+       }
+
+       @GET
+       @Produces(MediaType.APPLICATION_JSON)
+       @Path("/{serviceId}")
+       @ApiOperation(value = "Get properties of a discovery service registered 
in ODF", httpMethod = "GET", notes = "Retrieve properties of a discovery 
service that is registered in ODF", response = Response.class)
+       @ApiResponses(value = {
+                       @ApiResponse(code = 200, message = "OK"),
+                       @ApiResponse(code = 404, message = "Not found"),
+                       @ApiResponse(code = 500, message = "Internal server 
error")
+       })
+       public Response getDiscoveryServiceProperties(
+                       @ApiParam(value = "Id string of discovery service", 
required = true)
+                       @PathParam("serviceId") String serviceId) {
+               logger.entering(DiscoveryServicesResource.class.getName(), 
"getDiscoveryServiceProperties");
+               DiscoveryServiceManager dsAdmin = new 
ODFFactory().create().getDiscoveryServiceManager();
+               Response response;
+               try {
+                       DiscoveryServiceProperties dsStatus = 
dsAdmin.getDiscoveryServiceProperties(serviceId);
+                       if (dsStatus == null) {
+                               response = 
Response.status(Status.NOT_FOUND).build();
+                       }
+                       else {
+                               try {
+                                       String json = 
JSONUtils.toJSON(dsStatus);
+                                       response = Response.ok(json).build();
+                               } catch (JSONException e) {
+                                       e.printStackTrace();
+                                       logger.info("Parse exception " + e);
+                                       response = 
RestUtils.createErrorResponse(e);
+                               }
+                       }
+               }
+               catch (ServiceNotFoundException snfe) {
+                       response = 
Response.status(Status.NOT_FOUND).entity(snfe.getMessage()).build();
+               }
+               return response;
+       }
+
+       @POST
+       @Consumes(MediaType.APPLICATION_JSON)
+       @ApiOperation(value = "Register a discovery service", httpMethod = 
"POST", notes = "Register a new service in ODF", response = Response.class)
+       @ApiResponses(value = {
+                       @ApiResponse(code = 200, message = "OK"),
+                       @ApiResponse(code = 400, message = "Bad Request"),
+                       @ApiResponse(code = 500, message = "Internal server 
error")
+       })
+       public Response registerDiscoveryService(
+                       @ApiParam(value = "ODF service definition", required = 
true) DiscoveryServiceProperties dsProperties) {
+               logger.entering(DiscoveryServicesResource.class.getName(), 
"registerDiscoveryService");
+               Response response;
+               try {
+                       DiscoveryServiceManager dsAdmin = new 
ODFFactory().create().getDiscoveryServiceManager();
+                       dsAdmin.createDiscoveryService(dsProperties);
+                       response = Response.ok().build();
+               } catch (ValidationException e) {
+                       e.printStackTrace();
+                       logger.info("Validation exception during setting of 
property " + e.getProperty());
+                       response = 
RestUtils.createErrorResponse(e.getErrorCause());
+               }
+               return response;
+       }
+
+       @PUT
+       @Consumes(MediaType.APPLICATION_JSON)
+       @Produces(MediaType.APPLICATION_JSON)
+       @ApiOperation(value = "Update properties of a discovery service", 
httpMethod = "POST", notes = "Update properties of a discovery service that is 
registered in ODF", response = Response.class)
+       @ApiResponses(value = {
+                       @ApiResponse(code = 200, message = "OK"),
+                       @ApiResponse(code = 400, message = "Bad Request"),
+                       @ApiResponse(code = 500, message = "Internal server 
error")
+       })
+       public Response updateDiscoveryService(
+                       @ApiParam(value = "ODF service definition", required = 
true) DiscoveryServiceProperties dsProperties) {
+               logger.entering(DiscoveryServicesResource.class.getName(), 
"updateDiscoveryService");
+               Response response;
+               try {
+                       DiscoveryServiceManager dsAdmin = new 
ODFFactory().create().getDiscoveryServiceManager();
+                       dsAdmin.replaceDiscoveryService(dsProperties);
+                       response = Response.ok().build();
+               }
+               catch (ServiceNotFoundException snfe) {
+                       response = 
Response.status(Status.NOT_FOUND).entity(snfe.getMessage()).build();
+               }
+               catch (ValidationException e) {
+                       e.printStackTrace();
+                       logger.info("Validation exception during setting of 
property " + e.getProperty());
+                       response = 
RestUtils.createErrorResponse(e.getErrorCause());
+               }
+               return response;
+       }
+
+       @DELETE
+       @Path("/{serviceId}")
+       @ApiOperation(value = "Delete a discovery service", httpMethod = 
"DELETE", notes = "Remove a registered service from ODF", response = 
Response.class)
+       @ApiResponses(value = {
+                       @ApiResponse(code = 200, message = "OK"),
+                       @ApiResponse(code = 400, message = "Bad Request"),
+                       @ApiResponse(code = 500, message = "Internal server 
error")
+       })
+       public Response deleteDiscoveryService(
+                       @ApiParam(value = "Id string of discovery service to be 
deleted", required = true)
+                       @PathParam("serviceId") String serviceId) {
+               logger.entering(DiscoveryServicesResource.class.getName(), 
"deleteDiscoveryService");
+               Response response;
+               try {
+                       DiscoveryServiceManager dsAdmin = new 
ODFFactory().create().getDiscoveryServiceManager();
+                       dsAdmin.deleteDiscoveryService(serviceId);
+                       response = Response.ok().build();
+               }
+               catch (ServiceNotFoundException snfe) {
+                       response = 
Response.status(Status.NOT_FOUND).entity(snfe.getMessage()).build();
+               }
+               catch (ValidationException e) {
+                       e.printStackTrace();
+                       logger.info("Validation exception during deletion. 
Property: " + e.getProperty());
+                       response = 
RestUtils.createErrorResponse(e.getErrorCause());
+               }
+               return response;
+       }
+
+       @GET
+       @Path("/{serviceId}/image")
+       @Produces("image/*")
+       @ApiOperation(value = "Get a discovery service logo", httpMethod = 
"GET", notes = "Retrieve image representing a discovery service", response = 
InputStream.class)
+       @ApiResponses(value = { @ApiResponse(code = 200, message = "OK"), 
@ApiResponse(code = 404, message = "Not found"), @ApiResponse(code = 500, 
message = "Internal server error") })
+       public Response getImage(
+                       @ApiParam(value = "ID of discovery service", required = 
true)
+                       @PathParam("serviceId") String serviceId) {
+
+               DiscoveryServiceManager dsAdmin = new 
ODFFactory().create().getDiscoveryServiceManager();
+               Response response = null;
+               InputStream is;
+               try {
+                       is = dsAdmin.getDiscoveryServiceImage(serviceId);
+                       if (is == null) {
+                               // should never happen
+                               response = 
Response.status(Status.NOT_FOUND).build();
+                       }
+                       else {
+                               response = Response.ok(is, "image/png").build();
+                       }
+               } catch (ServiceNotFoundException snfe) {
+                       response = 
Response.status(Status.NOT_FOUND).entity(snfe.getMessage()).build();
+               }
+               return response;
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/6d19e129/odf/odf-web/src/main/java/org/apache/atlas/odf/admin/rest/resources/EngineResource.java
----------------------------------------------------------------------
diff --git 
a/odf/odf-web/src/main/java/org/apache/atlas/odf/admin/rest/resources/EngineResource.java
 
b/odf/odf-web/src/main/java/org/apache/atlas/odf/admin/rest/resources/EngineResource.java
new file mode 100755
index 0000000..d6cd37d
--- /dev/null
+++ 
b/odf/odf-web/src/main/java/org/apache/atlas/odf/admin/rest/resources/EngineResource.java
@@ -0,0 +1,167 @@
+/**
+ *
+ * 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.atlas.odf.admin.rest.resources;
+
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.apache.atlas.odf.api.engine.SystemHealth;
+import org.apache.atlas.odf.api.utils.ODFLogConfig;
+import org.apache.wink.json4j.JSONException;
+
+import org.apache.atlas.odf.admin.log.LoggingHandler;
+import org.apache.atlas.odf.admin.rest.RestUtils;
+import org.apache.atlas.odf.api.ODFFactory;
+import org.apache.atlas.odf.api.engine.ODFEngineOptions;
+import org.apache.atlas.odf.api.engine.ODFStatus;
+import org.apache.atlas.odf.api.engine.ODFVersion;
+import org.apache.atlas.odf.api.engine.ServiceRuntimesInfo;
+import org.apache.atlas.odf.json.JSONUtils;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+
+@Path("/engine")
+@Api(value = "/engine", description = "Monitor and control the ODF engine", 
produces = MediaType.APPLICATION_JSON)
+public class EngineResource {
+       final static LoggingHandler REST_LOG_HANDLER = new LoggingHandler();
+
+       static {
+               //initialize log config and log handler to cache logs
+               ODFLogConfig.run();
+               Logger rootLogger = Logger.getLogger("org.apache.atlas.odf");
+               REST_LOG_HANDLER.setLevel(Level.ALL);
+               rootLogger.addHandler(REST_LOG_HANDLER);
+       }
+
+       private Logger logger = 
Logger.getLogger(EngineResource.class.getName());
+
+       @POST
+       @Path("shutdown")
+       @Consumes(MediaType.APPLICATION_JSON)
+       @Produces(MediaType.APPLICATION_JSON)
+       @ApiOperation(value = "Shutdown ODF engine", httpMethod = "POST", notes 
= "Shutdown ODF engine, purge all scheduled analysis requests from the queues, 
and cancel all running analysis requests (for debugging purposes only)", 
response = Response.class)
+       @ApiResponses(value = { @ApiResponse(code = 200, message = "OK"), 
@ApiResponse(code = 500, message = "Internal server error") })
+       public Response shutdown(@ApiParam(value = "Engine options", 
defaultValue = "false", required = true) ODFEngineOptions engineOptions) {
+               logger.entering(EngineResource.class.getName(), "shutdown");
+               logger.log(Level.INFO, "Restart option is ", 
engineOptions.isRestart());
+               new 
ODFFactory().create().getEngineManager().shutdown(engineOptions);
+               return Response.ok().build();
+       }
+
+       @GET
+       @Path("health")
+       @Produces(MediaType.APPLICATION_JSON)
+       @ApiOperation(value = "Get health status", httpMethod = "GET", notes = 
"Check the health status of ODF", response = SystemHealth.class)
+       @ApiResponses(value = { @ApiResponse(code = 200, message = "OK"), 
@ApiResponse(code = 400, message = "Bad Request"), @ApiResponse(code = 500, 
message = "Internal server error") })
+       public Response healthCheck() {
+               logger.entering(EngineResource.class.getName(), "healthCheck");
+               SystemHealth health = new 
ODFFactory().create().getEngineManager().checkHealthStatus();
+               Status status = Status.OK;
+               try {
+                       return 
Response.status(status).entity(JSONUtils.toJSON(health)).type(MediaType.APPLICATION_JSON).build();
+               } catch (JSONException e) {
+                       e.printStackTrace();
+                       logger.info("Parse exception " + e);
+                       return RestUtils.createErrorResponse(e);
+               }
+       }
+
+       @GET
+       @Path("runtimes")
+       @Produces(MediaType.APPLICATION_JSON)
+       @ApiOperation(value = "Get info about the available runtimes", 
httpMethod = "GET", notes = "Get information about all runtimes running 
discovery services", response = SystemHealth.class)
+       @ApiResponses(value = { @ApiResponse(code = 200, message = "OK"), 
@ApiResponse(code = 400, message = "Bad Request"), @ApiResponse(code = 500, 
message = "Internal server error") })
+       public Response getRuntimesInfo() {
+               logger.entering(EngineResource.class.getName(), 
"getRuntimesInfo");
+               ServiceRuntimesInfo sri = new 
ODFFactory().create().getEngineManager().getRuntimesInfo();
+               Status status = Status.OK;
+               try {
+                       return 
Response.status(status).entity(JSONUtils.toJSON(sri)).type(MediaType.APPLICATION_JSON).build();
+               } catch (JSONException e) {
+                       e.printStackTrace();
+                       logger.info("Parse exception " + e);
+                       return RestUtils.createErrorResponse(e);
+               } finally {
+                       logger.exiting(EngineResource.class.getName(), 
"getRuntimesInfo");
+               }
+       }
+
+       @GET
+       @Path("status")
+       @Produces(MediaType.APPLICATION_JSON)
+       @ApiOperation(value = "Get current status", httpMethod = "GET", notes = 
"Retrieve status of the messaging subsystem and the internal thread manager", 
response = ODFStatus.class)
+       @ApiResponses(value = { @ApiResponse(code = 200, message = "OK"), 
@ApiResponse(code = 500, message = "Internal server error") })
+       public Response getStatus() throws IOException {
+               logger.entering(EngineResource.class.getName(), "getStatus");
+               try {
+                       ODFStatus odfStatus = new 
ODFFactory().create().getEngineManager().getStatus();
+                       return 
Response.status(Status.OK).entity(JSONUtils.toJSON(odfStatus)).type(MediaType.APPLICATION_JSON).build();
+               } catch (Exception exc) {
+                       logger.log(Level.INFO, "An exception occurred while 
getting the request status", exc);
+                       return RestUtils.createErrorResponse(exc);
+               }
+       }
+
+       @GET
+       @Path("log")
+       @Produces(MediaType.TEXT_PLAIN)
+       @ApiOperation(value = "Get current application log", httpMethod = 
"GET", notes = "Retrieve logs of the ODF instance", response = ODFStatus.class)
+       @ApiResponses(value = { @ApiResponse(code = 200, message = "OK"), 
@ApiResponse(code = 500, message = "Internal server error") })
+       public Response getLog(@QueryParam("numberOfLogs") Integer 
numberOfLogs, @QueryParam("logLevel") String logLevel) throws IOException {
+               logger.entering(EngineResource.class.getName(), "getLog");
+               try {
+                       Level level = Level.ALL;
+                       if (logLevel != null) {
+                               level = Level.parse(logLevel);
+                       }
+                       return 
Response.status(Status.OK).entity(REST_LOG_HANDLER.getFormattedCachedLog(numberOfLogs,
 level)).type(MediaType.TEXT_PLAIN).build();
+               } catch (Exception exc) {
+                       logger.log(Level.INFO, "An exception occurred while 
getting the ODF log", exc);
+                       return RestUtils.createErrorResponse(exc);
+               }
+       }
+
+       @GET
+       @Path("version")
+       @Produces(MediaType.APPLICATION_JSON)
+       @ApiOperation(value = "Get the ODF build version", httpMethod = "GET", 
notes = "The version is of the form versionnumber-buildid, e.g., 0.1.0-154", 
response = ODFVersion.class)
+       @ApiResponses(value = { @ApiResponse(code = 200, message = "OK"), 
@ApiResponse(code = 500, message = "Internal server error") })
+       public Response getVersion() {
+               try {
+                       ODFVersion version = new 
ODFFactory().create().getEngineManager().getVersion();
+                       Status status = Status.OK;
+                       return 
Response.status(status).entity(JSONUtils.toJSON(version)).type(MediaType.APPLICATION_JSON).build();
+               } catch (Exception exc) {
+                       logger.log(Level.INFO, "An exception occurred while 
getting the version", exc);
+                       return RestUtils.createErrorResponse(exc);
+               }
+
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/6d19e129/odf/odf-web/src/main/java/org/apache/atlas/odf/admin/rest/resources/ImportResource.java
----------------------------------------------------------------------
diff --git 
a/odf/odf-web/src/main/java/org/apache/atlas/odf/admin/rest/resources/ImportResource.java
 
b/odf/odf-web/src/main/java/org/apache/atlas/odf/admin/rest/resources/ImportResource.java
new file mode 100755
index 0000000..ef489a8
--- /dev/null
+++ 
b/odf/odf-web/src/main/java/org/apache/atlas/odf/admin/rest/resources/ImportResource.java
@@ -0,0 +1,89 @@
+/**
+ *
+ * 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.atlas.odf.admin.rest.resources;
+
+import java.util.logging.Logger;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.apache.atlas.odf.api.metadata.importer.JDBCMetadataImportResult;
+import org.apache.atlas.odf.api.metadata.importer.JDBCMetadataImporter;
+import org.apache.wink.json4j.JSONException;
+import org.apache.wink.json4j.JSONObject;
+
+import org.apache.atlas.odf.admin.rest.RestUtils;
+import org.apache.atlas.odf.api.ODFFactory;
+import org.apache.atlas.odf.api.metadata.importer.MetadataImportException;
+import org.apache.atlas.odf.api.metadata.models.JDBCConnection;
+import org.apache.atlas.odf.json.JSONUtils;
+
+@Path("/import")
+public class ImportResource {
+       private Logger logger = 
Logger.getLogger(ImportResource.class.getName());
+
+       @POST
+       @Consumes(MediaType.APPLICATION_JSON)
+       @Produces(MediaType.APPLICATION_JSON)
+       public Response doImport(String parameterString) {
+               logger.entering(ImportResource.class.getName(), "import");
+               try {
+                       JSONObject parameter = new JSONObject(parameterString);
+
+                       Object jdbcObj = parameter.get("jdbcString");
+                       Object userObj = parameter.get("user");
+                       Object passwordObj = parameter.get("password");
+                       Object dbObj = parameter.get("database");
+                       Object schemaObj = parameter.get("schema");
+                       Object tableObj = parameter.get("table");
+
+                       if (jdbcObj == null || userObj == null || passwordObj 
== null) {
+                               return 
RestUtils.createErrorResponse("jdbcString, user, password, database, schema and 
table are required!");
+                       }
+
+                       String user = (String) userObj;
+                       String password = (String) passwordObj;
+                       String jdbcString = (String) jdbcObj;
+                       String db = (String) dbObj;
+                       String schema = (String) schemaObj;
+                       String table = (String) tableObj;
+
+                       JDBCMetadataImporter importer = new 
ODFFactory().create().getJDBCMetadataImporter();
+                       JDBCConnection conn = new JDBCConnection();
+                       conn.setJdbcConnectionString(jdbcString);
+                       conn.setUser(user);
+                       conn.setPassword(password);
+
+                       JDBCMetadataImportResult result = null;
+                       try {
+                               result = importer.importTables(conn, db, 
schema, table);
+                       } catch (MetadataImportException ex) {
+                               return 
RestUtils.createErrorResponse(ex.getMessage());
+                       }
+
+                       if (result == null) {
+                               return Response.serverError().build();
+                       }
+
+                       return Response.ok(JSONUtils.toJSON(result)).build();
+               } catch (JSONException e) {
+                       return RestUtils.createErrorResponse(e.getMessage());
+               }
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/6d19e129/odf/odf-web/src/main/java/org/apache/atlas/odf/admin/rest/resources/MetadataResource.java
----------------------------------------------------------------------
diff --git 
a/odf/odf-web/src/main/java/org/apache/atlas/odf/admin/rest/resources/MetadataResource.java
 
b/odf/odf-web/src/main/java/org/apache/atlas/odf/admin/rest/resources/MetadataResource.java
new file mode 100755
index 0000000..9daf09a
--- /dev/null
+++ 
b/odf/odf-web/src/main/java/org/apache/atlas/odf/admin/rest/resources/MetadataResource.java
@@ -0,0 +1,246 @@
+/**
+ *
+ * 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.atlas.odf.admin.rest.resources;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.apache.wink.json4j.JSONException;
+import org.apache.wink.json4j.JSONObject;
+
+import org.apache.atlas.odf.admin.rest.RestUtils;
+import org.apache.atlas.odf.api.metadata.InternalMetaDataUtils;
+import org.apache.atlas.odf.api.metadata.MetaDataObjectReference;
+import org.apache.atlas.odf.api.metadata.MetadataStore;
+import org.apache.atlas.odf.api.metadata.MetadataStoreException;
+import org.apache.atlas.odf.api.metadata.models.MetaDataObject;
+import org.apache.atlas.odf.api.ODFFactory;
+import org.apache.atlas.odf.json.JSONUtils;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+
+@Path("/metadata")
+@Api(value = "/metadata", description = "Populate and query metadata 
repository", produces = MediaType.APPLICATION_JSON)
+public class MetadataResource {
+       private Logger logger = 
Logger.getLogger(MetadataResource.class.getName());
+
+       @GET
+       @Path("/connectiontest")
+       public Response testConnection() {
+               try {
+                       MetadataStore mds = new 
ODFFactory().create().getMetadataStore();
+                       MetadataStore.ConnectionStatus status = 
mds.testConnection();
+                       switch (status) {
+                       case OK:
+                               return Response.ok().build();
+                       case AUTHORIZATION_FAILED:
+                               return 
Response.status(Status.UNAUTHORIZED).build();
+                       case UNREACHABLE:
+                               return 
Response.status(Status.NOT_FOUND).build();
+                       default:
+                               return 
Response.status(Status.INTERNAL_SERVER_ERROR).build();
+                       }
+               } catch (Exception e) {
+                       logger.log(Level.WARNING, "An exception occurred while 
getting metatdata store properties", e);
+                       return RestUtils.createErrorResponse(e);
+               }
+       }
+
+       @GET
+       @Produces(MediaType.APPLICATION_JSON)
+       @ApiOperation(value = "Get metadata store properties", httpMethod = 
"GET", notes = "Retrieve type and URL of underlying metadata store", response = 
Response.class)
+       @ApiResponses(value = { @ApiResponse(code = 200, message = "OK"), 
@ApiResponse(code = 500, message = "Internal server error") })
+       public Response getMetadataStoreProperties() {
+               try {
+                       JSONObject result = new JSONObject();
+                       MetadataStore mds = new 
ODFFactory().create().getMetadataStore();
+                       Hashtable<Object, Object> propertyHashtable = 
(Hashtable<Object, Object>) mds.getProperties();
+                       for (Object propKey : propertyHashtable.keySet()) {
+                               result.put((String) propKey, (String) 
propertyHashtable.get(propKey));
+                       }
+                       String s = result.write();
+                       return Response.ok(s).build();
+               } catch (Exception e) {
+                       logger.log(Level.WARNING, "An exception occurred while 
getting metatdata store properties", e);
+                       return RestUtils.createErrorResponse(e);
+               }
+       }
+
+       @GET
+       @Path("/referencetypes")
+       @Produces(MediaType.APPLICATION_JSON)
+       @ApiOperation(value = "Get list of available reference types", 
httpMethod = "GET", notes = "Retrieve list of supported metadata object 
reference types", responseContainer="List", response = MetaDataObject.class)
+       @ApiResponses(value = { @ApiResponse(code = 200, message = "OK"), 
@ApiResponse(code = 500, message = "Internal server error") })
+       public Response getReferenceTypes() {
+               JSONObject result = new JSONObject();
+               List<String> referenceTypes = null;
+               try {
+                       MetadataStore mds = new 
ODFFactory().create().getMetadataStore();
+                       referenceTypes = mds.getReferenceTypes();
+                       result = JSONUtils.toJSONObject(referenceTypes);
+                       return Response.ok(result.write()).build();
+               } catch (JSONException e) {
+                       logger.warning("Parse exception " + e.getMessage() + " 
Parsed object: " + referenceTypes);
+                       return RestUtils.createErrorResponse(e);
+               }
+       }
+
+       @GET
+       @Path("/asset/{assetReference}")
+       @Produces(MediaType.APPLICATION_JSON)
+       @ApiOperation(value = "Retrieve asset by reference", httpMethod = 
"GET", notes = "Retrieve object from metadata repository", response = 
MetaDataObject.class)
+       @ApiResponses(value = { @ApiResponse(code = 200, message = "OK"), 
@ApiResponse(code = 500, message = "Internal server error") })
+       public Response retrieveAsset(@ApiParam(value = "Metadata object 
reference id", required = true) @PathParam("assetReference") String 
assetReference) {
+               JSONObject result;
+               try {
+                       MetaDataObjectReference ref = 
JSONUtils.fromJSON(assetReference, MetaDataObjectReference.class);
+                       MetadataStore mds = new 
ODFFactory().create().getMetadataStore();
+                       MetaDataObject mdo = mds.retrieve(ref);
+                       if (mdo != null) {
+                               result = JSONUtils.toJSONObject(mdo);
+                       } else {
+                               // Return empty JSON document to indicate that 
the result should be null.
+                               result = new JSONObject();
+                       }
+                       return Response.ok(result.write()).build();
+               } catch (JSONException e) {
+                       logger.warning("Parse exception " + e.getMessage() + " 
Parsed object: " + assetReference);
+                       return RestUtils.createErrorResponse(e);
+               }
+       }
+
+       @GET
+       @Path("/asset/{assetReference}/{referenceType}")
+       @Produces(MediaType.APPLICATION_JSON)
+       @ApiOperation(value = "Retrieve objects referenced by an asset", 
httpMethod = "GET", notes = "Retrieve referenced metadata objects by reference 
type", responseContainer="List", response = MetaDataObject.class)
+       @ApiResponses(value = { @ApiResponse(code = 200, message = "OK"), 
@ApiResponse(code = 500, message = "Internal server error") })
+       public Response retrieveAssetReferences(
+                       @ApiParam(value = "Metadata object reference", required 
= true) @PathParam("assetReference") String assetReference,
+                       @ApiParam(value = "Reference type name (including 
'PARENT' and 'CHILDREN')", required = true) @PathParam("referenceType") String 
referenceType) {
+               try {
+                       MetaDataObjectReference ref = 
JSONUtils.fromJSON(assetReference, MetaDataObjectReference.class);
+                       MetadataStore mds = new 
ODFFactory().create().getMetadataStore();
+                       List<MetaDataObject> referencedObjects = new 
ArrayList<MetaDataObject>();
+                       if 
(InternalMetaDataUtils.ODF_PARENT_REFERENCE.equals(referenceType.toUpperCase()))
 {
+                               MetaDataObject parent = 
mds.getParent(mds.retrieve(ref));
+                               if (parent != null) {
+                                       referencedObjects.add(parent);
+                               }
+                       } else if 
(InternalMetaDataUtils.ODF_CHILDREN_REFERENCE.toString().equals(referenceType.toUpperCase()))
 {
+                               referencedObjects = 
mds.getChildren(mds.retrieve(ref));
+                       } else {
+                               referencedObjects = 
mds.getReferences(referenceType.toUpperCase(), mds.retrieve(ref));
+                       }
+                       List<JSONObject> jsons = new ArrayList<JSONObject>();
+                       for (MetaDataObject obj : referencedObjects) {
+                               jsons.add(JSONUtils.toJSONObject(obj));
+                       }
+                       String result = JSONUtils.toJSON(jsons);
+                       logger.log(Level.FINE, "Serialized JSON: {0}", result);
+                       return Response.ok(result).build();
+               } catch (JSONException e) {
+                       logger.warning("Parse exception " + e.getMessage() + " 
Parsed object: " + assetReference);
+                       return RestUtils.createErrorResponse(e);
+               }
+       }
+
+       @GET
+       @Path("/sampledata")
+       @ApiOperation(value = "Create sample data", httpMethod = "GET", notes = 
"Populate metadata repository with ODF sample metadata", response = 
Response.class)
+       @ApiResponses(value = { @ApiResponse(code = 200, message = "OK"), 
@ApiResponse(code = 500, message = "Internal server error") })
+       public Response createSampleData() {
+               try {
+                       MetadataStore mds = new 
ODFFactory().create().getMetadataStore();
+                       mds.createSampleData();
+                       return Response.ok().build();
+               } catch (Exception exc) {
+                       exc.printStackTrace();
+                       throw new RuntimeException(exc);
+               }
+       }
+
+       @POST
+       @Path("/resetalldata")
+       public Response resetAllData() {
+               try {
+                       MetadataStore mds = new 
ODFFactory().create().getMetadataStore();
+                       mds.resetAllData();
+                       return Response.ok().build();
+               } catch (Exception e) {
+                       logger.log(Level.WARNING, "An exception occurred while 
resetting metatdata store", e);
+                       return RestUtils.createErrorResponse(e);
+               }
+       }
+
+       @GET
+       @Path("/search")
+       @Produces(MediaType.APPLICATION_JSON)
+       @ApiOperation(value = "Query metadata repository", httpMethod = "GET", 
notes = "Search for objects in metadata repository", responseContainer="List", 
response = MetaDataObjectReference.class)
+       @ApiResponses(value = {
+                       @ApiResponse(code = 200, message = "OK"),
+                       @ApiResponse(code = 400, message = "Bad Request"),
+                       @ApiResponse(code = 500, message = "Internal server 
error") })
+       public Response search(@ApiParam(value = "Query to be sent to metadata 
repository (refer to Atlas query notation)", required = true) 
@QueryParam("query") String query,
+                       @ApiParam(value = "Type of results to be returned, 
'objects' vs. 'references'", required = false) @QueryParam("resulttype") String 
resultType) {
+               List<MetaDataObjectReference> queryResults;
+               try {
+                       MetadataStore mds = new 
ODFFactory().create().getMetadataStore();
+                       try {
+                               queryResults = mds.search(query);
+                       } catch(MetadataStoreException e) {
+                               logger.log(Level.WARNING, 
MessageFormat.format("Error processing query ''{0}''.", query), e);
+                               return 
Response.status(Status.BAD_REQUEST).build();
+                       }
+                       List<JSONObject> jsons = new ArrayList<JSONObject>();
+                       if ((resultType != null) && 
resultType.equals("references")) {
+                               for (MetaDataObjectReference ref : 
queryResults) {
+                                       jsons.add(JSONUtils.toJSONObject(ref));
+                               }
+                       } else {
+                               // TODO very slow, retrieve results in bulk ?!?
+                               //FIXME serialization of each object on its own 
is necessary because of a jackson issue 
(https://github.com/FasterXML/jackson-databind/issues/336)
+                               //this should be replaced by a custom 
objectmapper initialization, issue #59 in gitlab
+                               for (MetaDataObjectReference ref : 
queryResults) {
+                                       MetaDataObject retrievedMdo = 
mds.retrieve(ref);
+                                       
jsons.add(JSONUtils.toJSONObject(retrievedMdo));
+                               }
+                       }
+                       String result = JSONUtils.toJSON(jsons);
+                       logger.log(Level.FINE, "Serialized JSON: {0}", result);
+                       return Response.ok(result).build();
+               } catch (Exception exc) {
+                       exc.printStackTrace();
+                       throw new RuntimeException(exc);
+               }
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/6d19e129/odf/odf-web/src/main/java/org/apache/atlas/odf/admin/rest/resources/SettingsResource.java
----------------------------------------------------------------------
diff --git 
a/odf/odf-web/src/main/java/org/apache/atlas/odf/admin/rest/resources/SettingsResource.java
 
b/odf/odf-web/src/main/java/org/apache/atlas/odf/admin/rest/resources/SettingsResource.java
new file mode 100755
index 0000000..e203774
--- /dev/null
+++ 
b/odf/odf-web/src/main/java/org/apache/atlas/odf/admin/rest/resources/SettingsResource.java
@@ -0,0 +1,128 @@
+/**
+ *
+ * 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.atlas.odf.admin.rest.resources;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+
+import java.text.MessageFormat;
+import java.util.HashMap;
+import java.util.logging.Logger;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.apache.atlas.odf.admin.rest.RestUtils;
+import org.apache.atlas.odf.api.settings.ODFSettings;
+import org.apache.atlas.odf.api.settings.SettingsManager;
+import org.apache.atlas.odf.api.settings.validation.ValidationException;
+import org.apache.atlas.odf.json.JSONUtils;
+import org.apache.wink.json4j.JSONException;
+
+import org.apache.atlas.odf.api.ODFFactory;
+
+@Path("/settings")
+@Api(value = "/settings", description = "View or update the settings of the 
Open Discovery Framework", produces = MediaType.APPLICATION_JSON)
+public class SettingsResource {
+
+       private Logger logger = 
Logger.getLogger(SettingsResource.class.getName());
+
+       @GET
+       @Produces(MediaType.APPLICATION_JSON)
+       @ApiOperation(value = "Retrieve settings", httpMethod = "GET", notes = 
"Retrieve current ODF settings", response = ODFSettings.class)
+       @ApiResponses(value = {
+                       @ApiResponse(code = 200, message = "OK"),
+                       @ApiResponse(code = 500, message = "Internal server 
error")
+       })
+       public Response getSettings() {
+               logger.entering(SettingsResource.class.getName(), "getConfig");
+               try {
+                       return Response.ok(JSONUtils.toJSON(new 
ODFFactory().create().getSettingsManager().getODFSettingsHidePasswords()), 
MediaType.APPLICATION_JSON).build();
+               } catch (JSONException e) {
+                       e.printStackTrace();
+                       logger.info("Parse exception " + e);
+                       return RestUtils.createErrorResponse(e);
+               }
+       }
+
+       @POST
+       @Path("/reset")
+       @Produces(MediaType.APPLICATION_JSON)
+       @Consumes(MediaType.APPLICATION_JSON)
+       @ApiOperation(value = "Reset settings", httpMethod = "POST", notes = 
"Reset ODF settings to the default", response = Response.class)
+       @ApiResponses(value = {
+                       @ApiResponse(code = 200, message = "OK"),
+                       @ApiResponse(code = 500, message = "Internal server 
error")
+       })
+       public Response resetSettings() {
+               logger.entering(SettingsResource.class.getName(), "getConfig");
+               new 
ODFFactory().create().getSettingsManager().resetODFSettings();
+               return Response.ok().build();
+       }
+
+       @PUT
+       @Produces(MediaType.APPLICATION_JSON)
+       @Consumes(MediaType.APPLICATION_JSON)
+       @ApiOperation(value = "Update settings", httpMethod = "PUT", notes = 
"Update ODF settings", response = ODFSettings.class)
+       @ApiResponses(value = {
+                       @ApiResponse(code = 200, message = "OK"),
+                       @ApiResponse(code = 400, message = "Bad Request"),
+                       @ApiResponse(code = 500, message = "Internal server 
error")
+       })
+       public Response changeSettings(@ApiParam(value = "ODF configuration 
options", required = true) ODFSettings odfConfig) {
+               logger.entering(SettingsResource.class.getName(), 
"changeConfig");
+               if (odfConfig == null) {
+                       return Response.status(Status.BAD_REQUEST).entity("The 
body must be a valid settings JSON.").build();
+               }
+
+               try {
+                       SettingsManager config = new 
ODFFactory().create().getSettingsManager();
+                       config.updateODFSettings(odfConfig);
+                       return 
Response.ok(JSONUtils.toJSON(config.getODFSettingsHidePasswords())).build();
+               } catch (ValidationException e) {
+                       e.printStackTrace();
+                       logger.info("Validation exception during setting of 
property " + e.getProperty());
+                       return RestUtils.createErrorResponse(e);
+               } catch (JSONException e1) {
+                       e1.printStackTrace();
+                       return 
RestUtils.createErrorResponse(MessageFormat.format("The provided input is not 
valid JSON in form {0}", getEmptyODFConfig()));
+               }
+       }
+
+       private String getEmptyODFConfig() {
+               ODFSettings odf = new ODFSettings();
+               odf.setUserDefined(new HashMap<String, Object>());
+               String emptyJSON = "";
+               try {
+                       emptyJSON = JSONUtils.toJSON(odf);
+               } catch (JSONException e2) {
+                       e2.printStackTrace();
+               }
+               return emptyJSON;
+       }
+
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/6d19e129/odf/odf-web/src/main/resources/org/apache/atlas/odf/images/activity_32.png
----------------------------------------------------------------------
diff --git 
a/odf/odf-web/src/main/resources/org/apache/atlas/odf/images/activity_32.png 
b/odf/odf-web/src/main/resources/org/apache/atlas/odf/images/activity_32.png
new file mode 100755
index 0000000..fabcc37
Binary files /dev/null and 
b/odf/odf-web/src/main/resources/org/apache/atlas/odf/images/activity_32.png 
differ

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/6d19e129/odf/odf-web/src/main/resources/org/apache/atlas/odf/images/applications_32.png
----------------------------------------------------------------------
diff --git 
a/odf/odf-web/src/main/resources/org/apache/atlas/odf/images/applications_32.png
 
b/odf/odf-web/src/main/resources/org/apache/atlas/odf/images/applications_32.png
new file mode 100755
index 0000000..1f3744b
Binary files /dev/null and 
b/odf/odf-web/src/main/resources/org/apache/atlas/odf/images/applications_32.png
 differ

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/6d19e129/odf/odf-web/src/main/resources/org/apache/atlas/odf/images/bar-chart_32.png
----------------------------------------------------------------------
diff --git 
a/odf/odf-web/src/main/resources/org/apache/atlas/odf/images/bar-chart_32.png 
b/odf/odf-web/src/main/resources/org/apache/atlas/odf/images/bar-chart_32.png
new file mode 100755
index 0000000..59a7ff8
Binary files /dev/null and 
b/odf/odf-web/src/main/resources/org/apache/atlas/odf/images/bar-chart_32.png 
differ

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/6d19e129/odf/odf-web/src/main/resources/org/apache/atlas/odf/images/world_32.png
----------------------------------------------------------------------
diff --git 
a/odf/odf-web/src/main/resources/org/apache/atlas/odf/images/world_32.png 
b/odf/odf-web/src/main/resources/org/apache/atlas/odf/images/world_32.png
new file mode 100755
index 0000000..4b9bcd3
Binary files /dev/null and 
b/odf/odf-web/src/main/resources/org/apache/atlas/odf/images/world_32.png differ

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/6d19e129/odf/odf-web/src/main/webapp/.gitignore
----------------------------------------------------------------------
diff --git a/odf/odf-web/src/main/webapp/.gitignore 
b/odf/odf-web/src/main/webapp/.gitignore
new file mode 100755
index 0000000..4cc0506
--- /dev/null
+++ b/odf/odf-web/src/main/webapp/.gitignore
@@ -0,0 +1,19 @@
+#
+#  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.
+#
+/odf-web.js
+/odf-web.js.map
+/odf-client.js
+/odf-client.js.map
+resources
+resources/**

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/6d19e129/odf/odf-web/src/main/webapp/WEB-INF/web.xml
----------------------------------------------------------------------
diff --git a/odf/odf-web/src/main/webapp/WEB-INF/web.xml 
b/odf/odf-web/src/main/webapp/WEB-INF/web.xml
new file mode 100755
index 0000000..9e16b0d
--- /dev/null
+++ b/odf/odf-web/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+~
+~ 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.
+-->
+<web-app id="WebApp_ID" version="3.0"
+       xmlns="http://java.sun.com/xml/ns/javaee"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd";>
+       <display-name>odf-admin</display-name>
+       <servlet>
+               <servlet-name>odf-admin-servlet</servlet-name>
+               
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+               <init-param>
+                       <param-name>javax.ws.rs.Application</param-name>
+                       
<param-value>org.apache.atlas.odf.admin.rest.ODFAdminApp</param-value>
+               </init-param>
+               <load-on-startup>1</load-on-startup>
+               <enabled>true</enabled>
+               <async-supported>false</async-supported>
+       </servlet>
+
+       <servlet-mapping>
+               <servlet-name>odf-admin-servlet</servlet-name>
+               <url-pattern>/odf/api/v1/*</url-pattern>
+       </servlet-mapping>
+
+       <security-constraint>
+               <web-resource-collection>
+                       <web-resource-name>Secure resources</web-resource-name>
+                       <url-pattern>/*</url-pattern>
+               </web-resource-collection>
+               <auth-constraint>
+                       <role-name>admin</role-name>
+                       <role-name>user</role-name>
+                       <role-name>moderator</role-name>
+               </auth-constraint>
+       </security-constraint>
+       <login-config>
+               <auth-method>BASIC</auth-method>
+               <realm-name>ODF Realm</realm-name>
+       </login-config>
+</web-app>

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/6d19e129/odf/odf-web/src/main/webapp/client_index.html
----------------------------------------------------------------------
diff --git a/odf/odf-web/src/main/webapp/client_index.html 
b/odf/odf-web/src/main/webapp/client_index.html
new file mode 100755
index 0000000..ea85c87
--- /dev/null
+++ b/odf/odf-web/src/main/webapp/client_index.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<!--
+  ~ 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.
+  -->
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Data lake application</title>
+</head>
+<body>
+   <div id="odf-toplevel-div" class="container-fluid">
+     Loading...
+   </div>
+   <script type="text/javascript" src="odf-config.js"></script>
+   <script type="text/javascript" src="odf-client.js"></script>
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/6d19e129/odf/odf-web/src/main/webapp/img/lg_proc.gif
----------------------------------------------------------------------
diff --git a/odf/odf-web/src/main/webapp/img/lg_proc.gif 
b/odf/odf-web/src/main/webapp/img/lg_proc.gif
new file mode 100755
index 0000000..7dd40ef
Binary files /dev/null and b/odf/odf-web/src/main/webapp/img/lg_proc.gif differ

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/6d19e129/odf/odf-web/src/main/webapp/index.html
----------------------------------------------------------------------
diff --git a/odf/odf-web/src/main/webapp/index.html 
b/odf/odf-web/src/main/webapp/index.html
new file mode 100755
index 0000000..f224997
--- /dev/null
+++ b/odf/odf-web/src/main/webapp/index.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<!--
+  ~ 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.
+  -->
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Open Discovery Framework</title>
+</head>
+<body>
+   <div id="odf-toplevel-div" class="container-fluid">
+     Loading...
+   </div>
+   <script type="text/javascript" src="odf-config.js"></script>
+   <script type="text/javascript" src="odf-web.js"></script>
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/6d19e129/odf/odf-web/src/main/webapp/odf-config.js
----------------------------------------------------------------------
diff --git a/odf/odf-web/src/main/webapp/odf-config.js 
b/odf/odf-web/src/main/webapp/odf-config.js
new file mode 100755
index 0000000..6bb4a47
--- /dev/null
+++ b/odf/odf-web/src/main/webapp/odf-config.js
@@ -0,0 +1,15 @@
+/**
+ * 
+ * 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.
+ */
+const API_PATH = "odf/api/v1/";

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/6d19e129/odf/odf-web/src/main/webapp/scripts/odf-analysis-request.js
----------------------------------------------------------------------
diff --git a/odf/odf-web/src/main/webapp/scripts/odf-analysis-request.js 
b/odf/odf-web/src/main/webapp/scripts/odf-analysis-request.js
new file mode 100755
index 0000000..67bb709
--- /dev/null
+++ b/odf/odf-web/src/main/webapp/scripts/odf-analysis-request.js
@@ -0,0 +1,473 @@
+/**
+ *
+ * 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.
+ */
+var $ = require("jquery");
+var bootstrap = require("bootstrap");
+
+var React = require("react");
+var ReactDOM = require("react-dom");
+var LinkedStateMixin = require('react-addons-linked-state-mixin');
+var ReactBootstrap = require("react-bootstrap");
+var AJAXCleanupMixin = require("./odf-mixins.js");
+var configurationStore = require("./odf-configuration-store.js");
+var metadataStore = require("./odf-utils.js").MetadataStore;
+var ODFGlobals = require("./odf-globals.js");
+
+var Button = ReactBootstrap.Button;
+var Row = ReactBootstrap.Row;
+var Col = ReactBootstrap.Col;
+var Table = ReactBootstrap.Table;
+var Modal = ReactBootstrap.Modal;
+var Input = ReactBootstrap.Input;
+var Alert = ReactBootstrap.Alert;
+var Panel = ReactBootstrap.Panel;
+var Label = ReactBootstrap.Label;
+var Input = ReactBootstrap.Input;
+var Image = ReactBootstrap.Image;
+
+var OdfAnalysisRequest = {
+       NewAnalysisRequestButton : React.createClass({
+
+               getInitialState : function(){
+                       return {showAnalysisRequestDialog : false};
+               },
+
+               open : function(){
+                       this.setState({showAnalysisRequestDialog: true});
+               },
+
+               onClose : function(){
+                       this.setState({showAnalysisRequestDialog: false});
+                       if(this.props.onClose){
+                               this.props.onClose();
+                       }
+               },
+
+               render : function() {
+                       return (
+                                       <span>
+                                               <Button 
bsStyle={this.props.bsStyle} onClick={this.open}>Start analysis (service 
sequence)</Button>
+                                               
<OdfAnalysisRequest.NewAnalysisRequestDialog 
show={this.state.showAnalysisRequestDialog} dataSetId={this.props.dataSetId} 
alertCallback={this.props.alertCallback} onClose={this.onClose}/>
+                                       </span>
+                       );
+               }
+
+       }),
+
+       NewAnalysisRequestDialog : React.createClass({
+
+         mixins : [AJAXCleanupMixin],
+
+         getInitialState : function() {
+           return ({config: null, discoveryServices: [], errorMessage: null, 
discoveryServiceSequence: []});
+         },
+
+         close : function() {
+                 this.clearDialogState();
+                 if(this.props.onClose){
+                         this.props.onClose();
+                 }
+         },
+
+         submitRequest : function() {
+               this.setState({requestInProgress : true});
+           var dataSet = this.refs.inputDataSet.getValue();
+           var discoveryServiceIDs = $.map(this.state.discoveryServiceSequence,
+              function(dsreg) {
+                 return dsreg.id;
+              }
+           );
+
+           var repositoryId = this.state.repositoryId;
+           var metadataObjectRef = {
+             repositoryId: repositoryId,
+             id: dataSet
+           };
+           var analysisRequest = {
+             dataSets: [metadataObjectRef],
+             discoveryServiceSequence: discoveryServiceIDs
+           };
+
+           // now post request
+           // clear alert
+           if(this.props.alertCallback){
+               this.props.alertCallback({type: "", message: ""});
+           }
+           var req = $.ajax({
+             url: ODFGlobals.analysisUrl,
+             contentType: "application/json",
+             dataType: 'json',
+             type: 'POST',
+             data: JSON.stringify(analysisRequest),
+             success: function(analysisResponse) {
+               if(!this.isMounted()){
+                       return;
+               }
+               if (analysisResponse.invalidRequest) {
+                 this.setState({errorMessage: analysisResponse.details, 
requestInProgress: false});
+               } else {
+                 var msg = "Analysis request was started. ID: " + 
analysisResponse.id;
+                 if(this.props.alertCallback){
+                   this.props.alertCallback({type: "success", message: msg});
+                 }
+                 this.close();
+               }
+             }.bind(this),
+             error: function(xhr, status, err) {
+               var msg = "Error while reading ODF services: " + err.toString();
+               this.setState({errorMessage: msg, requestInProgress: false});
+             }.bind(this)
+           });
+
+           this.storeAbort(req.abort);
+         },
+
+         componentDidMount : function() {
+                 this.loadDiscoveryServices();
+         },
+
+         loadDiscoveryServices : function() {
+           var req = configurationStore.readConfig(
+             function(config) {
+               if(!this.isMounted()){
+                       return;
+               }
+               this.setState({config: config});
+               // clear alert
+               if(this.props.alertCallback){
+                       this.props.alertCallback({type: "", message: ""});
+               }
+               var req2 = $.ajax({
+                 url: ODFGlobals.servicesUrl,
+                 dataType: 'json',
+                 type: 'GET',
+                 success: function(data) {
+                       if(!this.isMounted()){
+                               return;
+                       }
+                   this.setState({discoveryServices: data});
+                 }.bind(this),
+                 error: function(xhr, status, err) {
+                   var msg = "Error while reading ODF services: " + 
err.toString();
+                   if(this.props.alertCallback){
+                           this.props.alertCallback({type: "danger", message: 
msg});
+                   }
+                }.bind(this)
+               });
+               this.storeAbort(req2.abort);
+             }.bind(this),
+             this.props.alertCallback
+           );
+
+           this.storeAbort(req.abort);
+         },
+
+         getDiscoveryServiceFromId : function(id) {
+             var servicesWithSameId = this.state.discoveryServices.filter(
+                function(dsreg) {
+                    return dsreg.id == id;
+                }
+             );
+             if (servicesWithSameId.length > 0) {
+               return servicesWithSameId[0];
+             }
+             return null;
+         },
+
+         processDiscoveryServiceSelection : function() {
+             var selection = 
this.refs.inputAvailableDiscoveryServices.getValue();
+             var dsreg = this.getDiscoveryServiceFromId(selection);
+             if (dsreg) {
+               var newSequence = this.state.discoveryServiceSequence.slice();
+               newSequence.push(dsreg);
+               this.setState({discoveryServiceSequence: newSequence});
+             }
+         },
+
+         clearDialogState : function() {
+             this.setState({discoveryServiceSequence: [], requestInProgress : 
false, });
+         },
+
+         render : function() {
+            var alert = null;
+            if (this.state.errorMessage) {
+               alert = <Alert 
bsStyle="danger">{this.state.errorMessage}</Alert>;
+            }
+            var servicesOptions = $.map(
+                   this.state.discoveryServices,
+                   function(dsreg) {
+                     return (<option key={dsreg.id} 
value={dsreg.id}>{dsreg.name}</option>);
+                   }.bind(this)
+               );
+
+            var discoveryServiceSequenceComponents = 
$.map(this.state.discoveryServiceSequence,
+                function(dsreg) {
+                   return <li key={dsreg.id}>{dsreg.name} ({dsreg.id})</li>
+                }
+            );
+
+            var waitingContainer = <div style={{position:"absolute", 
width:"100%", height:"100%", left:"50%", top: "30%"}}><Image 
src="img/lg_proc.gif" rounded /></div>;
+            if(!this.state.requestInProgress){
+                waitingContainer = null;
+            }
+
+            return (
+              <Modal show={this.props.show} onHide={this.close}>
+                <Modal.Header closeButton>
+                   <Modal.Title>Start analysis (specify service 
sequence)</Modal.Title>
+                </Modal.Header>
+                <Modal.Body>
+                       {waitingContainer}
+                   {alert}
+                   <Input type="text" ref="inputDataSet" label="Data Set" 
value={this.props.dataSetId} readOnly={this.props.dataSetId}></Input>
+                   <hr/>
+                   Select a service from the "Available Services"
+                   dropdown to append it to the sequence. Repeat selection to 
run multiple services for the data set.
+                   <Input type="select" 
onChange={this.processDiscoveryServiceSelection} 
ref="inputAvailableDiscoveryServices" label="Available Services">
+                     <option key="emptySelection">&lt;Select a 
service...&gt;</option>
+                     {servicesOptions}
+                   </Input>
+                   <strong>Service Sequence</strong>
+                   <ol>{discoveryServiceSequenceComponents}</ol>
+                   <hr />
+                   <Button bsStyle="warning" 
onClick={this.clearDialogState}>Clear Sequence</Button>
+               </Modal.Body>
+               <Modal.Footer>
+               <Button onClick={this.submitRequest} 
bsStyle="primary">Submit</Button>
+               <Button onClick={this.close} >Cancel</Button>
+               </Modal.Footer>
+              </Modal>
+            );
+         }
+
+       }),
+
+       NewCreateAnnotationsButton : React.createClass({
+
+               getInitialState : function(){
+                       return {showCreateAnnotationsDialog : false};
+               },
+
+               open : function(){
+                       this.setState({showCreateAnnotationsDialog: true});
+               },
+
+               onClose : function(){
+                       this.setState({showCreateAnnotationsDialog: false});
+                       if(this.props.onClose){
+                               this.props.onClose();
+                       }
+               },
+
+               render : function() {
+                       return (
+                                       <span>
+                                               <Button 
bsStyle={this.props.bsStyle} onClick={this.open}>Start analysis (annotation 
types)</Button>
+                                               
<OdfAnalysisRequest.NewCreateAnnotationsDialog 
show={this.state.showCreateAnnotationsDialog} dataSetId={this.props.dataSetId} 
alertCallback={this.props.alertCallback} onClose={this.onClose}/>
+                                       </span>
+                       );
+               }
+
+       }),
+
+       NewCreateAnnotationsDialog : React.createClass({
+
+                 mixins : [AJAXCleanupMixin],
+
+                 getInitialState : function() {
+                   return ({config: null, annotationTypes: [], errorMessage: 
null, analysisTypeSelection: []});
+                 },
+
+                 close : function() {
+                         this.clearDialogState();
+                         if(this.props.onClose){
+                                 this.props.onClose();
+                         }
+                 },
+
+                 submitRequest : function() {
+                       this.setState({requestInProgress : true});
+                   var dataSet = this.refs.inputDataSet.getValue();
+                   var annotationTypeIDs = 
$.map(this.state.analysisTypeSelection,
+                      function(annotationTypeId) {
+                         return annotationTypeId;
+                      }
+                   );
+
+                   var repositoryId = this.state.repositoryId;
+                   var metadataObjectRef = {
+                     repositoryId: repositoryId,
+                     id: dataSet
+                   };
+                   var analysisRequest = {
+                     dataSets: [metadataObjectRef],
+                     annotationTypes: annotationTypeIDs
+                   };
+
+                   // now post request
+                   // clear alert
+                   if(this.props.alertCallback){
+                       this.props.alertCallback({type: "", message: ""});
+                   }
+                   var req = $.ajax({
+                     url: ODFGlobals.analysisUrl,
+                     contentType: "application/json",
+                     dataType: 'json',
+                     type: 'POST',
+                     data: JSON.stringify(analysisRequest),
+                     success: function(analysisResponse) {
+                       if(!this.isMounted()){
+                               return;
+                       }
+                       if (analysisResponse.invalidRequest) {
+                         this.setState({errorMessage: 
analysisResponse.details, requestInProgress: false});
+                       } else {
+                         var msg = "Analysis request was started. ID: " + 
analysisResponse.id;
+                         if(this.props.alertCallback){
+                           this.props.alertCallback({type: "success", message: 
msg});
+                         }
+                         this.close();
+                       }
+                     }.bind(this),
+                     error: function(xhr, status, err) {
+                       var msg = "Error starting discovery request: " + 
err.toString();
+                       this.setState({errorMessage: msg, requestInProgress: 
false});
+                     }.bind(this)
+                   });
+
+                   this.storeAbort(req.abort);
+                 },
+
+                 componentDidMount : function() {
+                         this.loadannotationTypes();
+                 },
+
+                 loadannotationTypes : function() {
+                   var req = configurationStore.readConfig(
+                     function(config) {
+                       if(!this.isMounted()){
+                               return;
+                       }
+                       this.setState({config: config});
+                       // clear alert
+                       if(this.props.alertCallback){
+                               this.props.alertCallback({type: "", message: 
""});
+                       }
+                       var req2 = $.ajax({
+                         url: ODFGlobals.servicesUrl,
+                         dataType: 'json',
+                         type: 'GET',
+                         success: function(data) {
+                               if(!this.isMounted()){
+                                       return;
+                               }
+                           var ids = [];
+                           $.each(data, function(key, dsreg){
+                                   $.each(dsreg.resultingAnnotationTypes, 
function(key, annotationTypeId){
+                                       if($.inArray(annotationTypeId,ids) == 
-1){
+                                               ids.push(annotationTypeId);
+                                       };
+                                   });
+                           });
+                           this.setState({annotationTypes: ids});
+                         }.bind(this),
+                         error: function(xhr, status, err) {
+                           var msg = "Error while reading ODF services: " + 
err.toString();
+                           if(this.props.alertCallback){
+                                   this.props.alertCallback({type: "danger", 
message: msg});
+                           }
+                        }.bind(this)
+                       });
+                       this.storeAbort(req2.abort);
+                     }.bind(this),
+                     this.props.alertCallback
+                   );
+                        metadataStore.getProperties(
+                                        function(data) {
+                                            this.setState({repositoryId: 
data.STORE_PROPERTY_ID});
+                                        }.bind(this)
+                        );
+                   this.storeAbort(req.abort);
+                 },
+
+                 processAnalysisTypeSelection : function() {
+                     var selection = 
this.refs.inputAvailableAnnotationTypes.getValue();
+                     if (selection) {
+                       var newSelection = 
this.state.analysisTypeSelection.slice();
+                       newSelection.push(selection);
+                       this.setState({analysisTypeSelection: newSelection});
+                     }
+                 },
+
+                 clearDialogState : function() {
+                     this.setState({analysisTypeSelection: [], 
requestInProgress : false, });
+                 },
+
+                 render : function() {
+                    var alert = null;
+                    if (this.state.errorMessage) {
+                       alert = <Alert 
bsStyle="danger">{this.state.errorMessage}</Alert>;
+                    }
+                    var analysisTypeOptions = $.map(
+                                   this.state.annotationTypes,
+                                   function(annotationTypeId) {
+                                     return (<option key={annotationTypeId} 
value={annotationTypeId}>{annotationTypeId}</option>);
+                                   }.bind(this)
+                               );
+
+                    var analysisTypeSelectionComponents = 
$.map(this.state.analysisTypeSelection,
+                        function(annotationTypeId) {
+                           return <li 
key={annotationTypeId}>{annotationTypeId}</li>
+                        }
+                    );
+
+                    var waitingContainer = <div style={{position:"absolute", 
width:"100%", height:"100%", left:"50%", top: "30%"}}><Image 
src="img/lg_proc.gif" rounded /></div>;
+                    if(!this.state.requestInProgress){
+                        waitingContainer = null;
+                    }
+
+                    return (
+                      <Modal show={this.props.show} onHide={this.close}>
+                        <Modal.Header closeButton>
+                           <Modal.Title>Start analysis (specify annotation 
types)</Modal.Title>
+                        </Modal.Header>
+                        <Modal.Body>
+                               {waitingContainer}
+                           {alert}
+                           <Input type="text" ref="inputDataSet" label="Data 
Set" value={this.props.dataSetId} readOnly={this.props.dataSetId}></Input>
+                           <hr/>
+                           Select an annotation type from the "Available 
Annotation Types"
+                           dropdown to append it to the list. Repeat selection 
to create multiple annotation types for the data set.
+                           <Input type="select" 
onChange={this.processAnalysisTypeSelection} 
ref="inputAvailableAnnotationTypes" label="Available Annotation Types">
+                             <option key="emptySelection">&lt;Select an 
annotation type...&gt;</option>
+                             {analysisTypeOptions}
+                           </Input>
+                           <strong>Selected Annotation Types</strong>
+                           <ol>{analysisTypeSelectionComponents}</ol>
+                           <hr />
+                           <Button bsStyle="warning" 
onClick={this.clearDialogState}>Clear Selection</Button>
+                       </Modal.Body>
+                       <Modal.Footer>
+                       <Button onClick={this.submitRequest} 
bsStyle="primary">Submit</Button>
+                       <Button onClick={this.close} >Cancel</Button>
+                       </Modal.Footer>
+                      </Modal>
+                    );
+                 }
+
+               })
+}
+
+
+module.exports = OdfAnalysisRequest;

Reply via email to