/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.usergrid.rest.system; import java.util.HashMap; import java.util.Map; import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; 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 org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import org.apache.usergrid.corepersistence.service.ConnectionService; import org.apache.usergrid.corepersistence.service.ConnectionServiceImpl; import org.apache.usergrid.corepersistence.service.StatusService; import org.apache.usergrid.corepersistence.util.CpNamingUtils; import org.apache.usergrid.persistence.core.scope.ApplicationScope; import org.apache.usergrid.persistence.index.query.Identifier; import org.apache.usergrid.persistence.index.utils.UUIDUtils; 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.RootResource; import org.apache.usergrid.rest.security.annotations.RequireSystemAccess; import com.google.common.base.Preconditions; import rx.Observable; import rx.schedulers.Schedulers; /** * system/index/otherstuff */ @Component @Scope( "singleton" ) @Produces( { MediaType.APPLICATION_JSON, "application/javascript", "application/x-javascript", "text/ecmascript", "application/ecmascript", "text/jscript" } ) public class ConnectionResource extends AbstractContextResource { private static final Logger logger = LoggerFactory.getLogger( ConnectionResource.class ); public ConnectionResource() { super(); } @RequireSystemAccess @POST @Path( "dedup/" + RootResource.APPLICATION_ID_PATH ) public ApiResponse rebuildIndexesPost( @PathParam( "applicationId" ) String applicationIdStr, @QueryParam( "callback" ) @DefaultValue( "callback" ) String callback ) throws Exception { logger.info( "Rebuilding all applications" ); final UUID applicationId = UUIDUtils.tryGetUUID( applicationIdStr ); Preconditions.checkNotNull( applicationId, "applicationId must be specified" ); return executeAndCreateResponse( applicationId, callback ); } @RequireSystemAccess @GET @Path( "dedup/{jobId: " + Identifier.UUID_REX + "}" ) public ApiResponse rebuildIndexesGet( @PathParam( "jobId" ) String jobId, @QueryParam( "callback" ) @DefaultValue( "callback" ) String callback ) throws Exception { logger.info( "Getting status for index jobs" ); Preconditions.checkNotNull( jobId, "query param jobId must not be null" ); final UUID jobUUID = UUIDUtils.tryGetUUID( jobId ); final StatusService.JobStatus job = getStatusService().getStatus( CpNamingUtils.MANAGEMENT_APPLICATION_ID, jobUUID ).toBlocking().lastOrDefault( null ); Preconditions.checkNotNull( job, "job with id '" + jobId + "' does not exist" ); return createResult( job, callback ); } private ConnectionService getConnectionService() { return injector.getInstance( ConnectionServiceImpl.class ); } private StatusService getStatusService() { return injector.getInstance( StatusService.class ); } /** * Execute the request and return the response. */ private ApiResponse executeAndCreateResponse( final UUID applicationId, final String callback ) { final Observable<ApplicationScope> applicationScopeObservable = Observable.just( CpNamingUtils.getApplicationScope( applicationId ) ); final UUID jobId = UUIDGenerator.newTimeUUID(); final StatusService statusService = getStatusService(); final ConnectionService connectionService = getConnectionService(); final AtomicLong count = new AtomicLong( 0 ); //start de duping and run in the background connectionService.deDupeConnections( applicationScopeObservable ).buffer( 10, TimeUnit.SECONDS, 1000 ) .doOnNext(buffer -> { final long runningTotal = count.addAndGet(buffer.size()); final Map<String, Object> status = new HashMap<String, Object>() {{ put("countProcessed", runningTotal); put("updatedTimestamp", System.currentTimeMillis()); }}; statusService.setStatus(CpNamingUtils.MANAGEMENT_APPLICATION_ID, jobId, StatusService.Status.INPROGRESS, status).toBlocking().lastOrDefault(null); }).doOnSubscribe(() -> { statusService.setStatus(CpNamingUtils.MANAGEMENT_APPLICATION_ID, jobId, StatusService.Status.STARTED, new HashMap<>()).toBlocking().lastOrDefault(null); }).doOnCompleted(() -> { final long runningTotal = count.get(); final Map<String, Object> status = new HashMap<String, Object>() {{ put("countProcessed", runningTotal); put("updatedTimestamp", System.currentTimeMillis()); }}; statusService.setStatus(CpNamingUtils.MANAGEMENT_APPLICATION_ID, jobId, StatusService.Status.COMPLETE, status).toBlocking().lastOrDefault(null); }).doOnError( (throwable) -> { logger.error("Error deduping connections", throwable); final Map<String, Object> status = new HashMap<String, Object>() {{ put("error", throwable.getMessage() ); }}; statusService.setStatus(CpNamingUtils.MANAGEMENT_APPLICATION_ID, jobId, StatusService.Status.FAILED, status).toBlocking().lastOrDefault(null);; } ).subscribeOn(Schedulers.newThread()).subscribe(); final StatusService.JobStatus status = new StatusService.JobStatus( jobId, StatusService.Status.STARTED, new HashMap<>( ) ); return createResult( status, callback ); } /** * Create a response with the specified data. * @param jobStatus * @param callback * @return */ private ApiResponse createResult(final StatusService.JobStatus jobStatus, final String callback){ final ApiResponse response = createApiResponse(); response.setAction( "de-dup connections" ); response.setProperty( "status", jobStatus ); response.setSuccess(); return response; } }