/* * * * Licensed to the Apache Software Foundation (ASF) under one or more * * contributor license agreements. 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. For additional information regarding * * copyright in this work, please see the NOTICE file in the top level * * directory of this distribution. * */ package org.apache.usergrid.rest.system; import com.fasterxml.jackson.jaxrs.json.annotation.JSONP; import org.apache.usergrid.corepersistence.service.StatusService; import org.apache.usergrid.persistence.EntityManager; import org.apache.usergrid.persistence.model.util.UUIDGenerator; import org.apache.usergrid.rest.AbstractContextResource; import org.apache.usergrid.rest.ApiResponse; import org.apache.usergrid.rest.security.annotations.RequireSystemAccess; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import javax.ws.rs.*; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.UriInfo; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; /** * Classy class class. */ @Component @Scope( "singleton" ) @Produces( { MediaType.APPLICATION_JSON, "application/javascript", "application/x-javascript", "text/ecmascript", "application/ecmascript", "text/jscript" } ) public class ApplicationsResource extends AbstractContextResource { private static final Logger logger = LoggerFactory.getLogger(ApplicationsResource.class); public ApplicationsResource() { logger.info( "ApplicationsResource initialized" ); } @RequireSystemAccess @DELETE @JSONP @Path( "{applicationId}" ) public ApiResponse clearApplication( @Context UriInfo ui, @PathParam("applicationId") UUID applicationId, @QueryParam( "confirmApplicationName" ) String confirmApplicationName, @QueryParam( "limit" ) int limit, @QueryParam( "callback" ) @DefaultValue( "callback" ) String callback ) throws Exception { if(confirmApplicationName == null){ throw new IllegalArgumentException("confirmApplicationName query parameter is required"); } final UUID jobId = UUIDGenerator.newTimeUUID(); final EntityManager em = emf.getEntityManager(applicationId); final String name = em.getApplication().getApplicationName(); if(!name.toLowerCase().equals(confirmApplicationName.toLowerCase())){ throw new IllegalArgumentException( "confirmApplicationName mismatch: " + confirmApplicationName + " does not equal " + name); } final StatusService statusService = injector.getInstance(StatusService.class); final ApiResponse response = createApiResponse(); response.setAction( "clear application" ); logger.info("clearing up application"); final Thread delete = new Thread() { @Override public void run() { final AtomicInteger itemsDeleted = new AtomicInteger(0); try { management.deleteAllEntities(applicationId, limit) .map(id -> itemsDeleted.incrementAndGet()) .doOnNext(count -> { if( count % 100 == 0 ){ Map<String,Object> map = new LinkedHashMap<>(); map.put("count",itemsDeleted.intValue()); final StatusService statusService = injector.getInstance(StatusService.class); statusService.setStatus(applicationId, jobId, StatusService.Status.INPROGRESS,map) .subscribe();//do not want to throw this exception } }) .doOnCompleted(() -> { Map<String, Object> map = new LinkedHashMap<>(); map.put("count", itemsDeleted.intValue()); final StatusService statusService = injector.getInstance(StatusService.class); statusService.setStatus(applicationId, jobId, StatusService.Status.COMPLETE, map) .toBlocking().lastOrDefault(null);//want to rethrow this exception }) .toBlocking().lastOrDefault(null);//expecting exception to be caught if job fails } catch ( Exception e ) { Map<String,Object> map = new LinkedHashMap<>(); map.put("exception",e); try { statusService.setStatus(applicationId, jobId, StatusService.Status.FAILED, map).toBlocking().lastOrDefault(null);//leave as subscribe if fails retry }catch (Exception subE){ logger.error("failed to update status {}",jobId,subE); } logger.error( "Failed to delete appid:{} jobid:{} count:{}",applicationId,jobId,itemsDeleted, e ); } } }; delete.setName("Delete for app : " + applicationId + " job: " + jobId); delete.setDaemon(true); delete.start(); try { //should throw exception if can't start statusService.setStatus(applicationId, jobId, StatusService.Status.STARTED, new LinkedHashMap<>()).toBlocking().lastOrDefault(null); }catch (Exception e){ logger.error("failed to set status for {}", jobId, e); } Map<String,Object> data = new HashMap<>(); data.put("jobId",jobId); data.put("status",StatusService.Status.STARTED); response.setData(data); response.setSuccess(); return response; } @RequireSystemAccess @GET @Path( "{applicationId}/job/{jobId}" ) public ApiResponse getStatus( @Context UriInfo ui, @PathParam("applicationId") UUID applicationId, @PathParam("jobId") UUID jobId, @QueryParam( "callback" ) @DefaultValue( "callback" ) String callback ) throws Exception{ final StatusService statusService = injector.getInstance(StatusService.class); final ApiResponse response = createApiResponse(); response.setAction( "clear application" ); StatusService.JobStatus jobStatus = statusService.getStatus(applicationId, jobId).toBlocking().lastOrDefault(null); Map<String,Object> data = new HashMap<>(); data.put("jobId",jobId); data.put( "status", jobStatus.getStatus().toString() ); data.put( "metadata", jobStatus.getData() ); response.setData(data); response.setSuccess(); return response; } }