blob: 4d6107e2b613b27a481cd8eb4ab624a9049a8f62 [file] [log] [blame]
/*
* 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.applications.kvm;
import com.google.inject.Injector;
import org.apache.commons.lang.StringUtils;
import org.apache.usergrid.persistence.map.MapKeyResults;
import org.apache.usergrid.persistence.map.MapManager;
import org.apache.usergrid.persistence.map.MapManagerFactory;
import org.apache.usergrid.persistence.map.MapScope;
import org.apache.usergrid.persistence.map.impl.MapScopeImpl;
import org.apache.usergrid.rest.AbstractContextResource;
import org.apache.usergrid.rest.ApiResponse;
import org.apache.usergrid.rest.applications.ServiceResource;
import org.apache.usergrid.services.ServiceManager;
import org.apache.usergrid.utils.JsonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import rx.Observable;
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.*;
@Component("org.apache.usergrid.rest.applications.kvm.KvmResource")
@Scope("prototype")
@Produces(MediaType.APPLICATION_JSON)
public class KvmResource extends AbstractContextResource {
private static final Logger logger = LoggerFactory.getLogger( KvmResource.class );
private final int PAGE_SIZE_MAX = 1000;
private final int PAGE_SIZE_DEFAULT = 10;
private final int BATCH_SIZE_MAX = 10;
ServiceManager services = null;
@Autowired
private Injector injector;
public KvmResource(){
if(logger.isTraceEnabled()) {
logger.trace("kvm.KvmResource");
}
}
@Override
public void setParent( AbstractContextResource parent ) {
super.setParent( parent );
if ( parent instanceof ServiceResource ) {
services = ( ( ServiceResource ) parent ).services;
}
}
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public ApiResponse createMap( @Context UriInfo ui,
Map<String, Object> json ) {
if( json.keySet().size() < 1 ){
throw new IllegalArgumentException("New map must be specified");
}
if( json.keySet().size() > 1 ){
throw new IllegalArgumentException("Only 1 map can be created at a time.");
}
enforceQueryFeature(ui);
final String mapName = json.keySet().iterator().next();
if( !(json.get(mapName) instanceof Map) ){
throw new IllegalArgumentException("New map details must be provided as a JSON object.");
}
final Map<String, Object> meta = (Map<String, Object>)json.get(mapName);
if(StringUtils.isEmpty(mapName)){
throw new IllegalArgumentException("Property mapName is required.");
}
if( json.containsKey("metadata") ){
json.remove("metadata");
}
final String appMap = services.getApplicationId().toString();
final MapScope mapScope = new MapScopeImpl(services.getApplication().asId(), appMap);
final MapManagerFactory mmf = injector.getInstance(MapManagerFactory.class);
final MapManager mapManager = mmf.createMapManager(mapScope);
final Map<String, Object> mapDetails = new HashMap<String, Object>(){{
put("metadata", new HashMap<String, Object>(){{
put("active", true);
put("modified", System.currentTimeMillis() );
}});
putAll(meta);
}};
mapManager.putString(mapName, JsonUtils.mapToJsonString(mapDetails));
// This used for response format
final Map<String, Object> map = new HashMap<>(2);
map.put(mapName, mapDetails);
ApiResponse response = createApiResponse();
response.setAction( "post" );
response.setApplication( services.getApplication() );
response.setParams( ui.getQueryParameters() );
response.setProperty("maps", map);
return response;
}
@GET
@Produces(MediaType.APPLICATION_JSON)
public ApiResponse getMaps( @Context UriInfo ui,
@QueryParam("limit") int limit,
@QueryParam("cursor") String cursor) {
limit = validateLimit(limit);
enforceQueryFeature(ui);
final String appMap = services.getApplicationId().toString();
final MapScope mapScope = new MapScopeImpl(services.getApplication().asId(), appMap);
final MapManagerFactory mmf = injector.getInstance(MapManagerFactory.class);
final MapManager mapManager = mmf.createMapManager(mapScope);
MapKeyResults mapKeyResults = mapManager.getKeys(cursor, limit );
Map<String, Object> results = new HashMap<>();
Observable.from(mapKeyResults.getKeys()).flatMap(key -> {
return Observable.just(results.put(key, JsonUtils.parse(mapManager.getString(key))));
}).toBlocking().lastOrDefault(null);
ApiResponse response = createApiResponse();
response.setAction( "get" );
response.setApplication( services.getApplication() );
response.setParams( ui.getQueryParameters() );
response.setProperty("maps", results);
if( StringUtils.isNotEmpty(mapKeyResults.getCursor())){
response.setProperty("cursor", mapKeyResults.getCursor());
}
return response;
}
@GET
@Path("{mapName}")
@Produces(MediaType.APPLICATION_JSON)
public ApiResponse getSingleMap( @Context UriInfo ui,
@QueryParam("limit") int limit,
@QueryParam("cursor") String cursor,
@PathParam("mapName") String mapName ) {
limit = validateLimit(limit);
enforceQueryFeature(ui);
final String appMap = services.getApplicationId().toString();
final MapScope mapScope = new MapScopeImpl(services.getApplication().asId(), appMap);
final MapManagerFactory mmf = injector.getInstance(MapManagerFactory.class);
final MapManager mapManager = mmf.createMapManager(mapScope);
Map<String, Object> map = new HashMap<>();
map.put(mapName, JsonUtils.parse(mapManager.getString(mapName)));
ApiResponse response = createApiResponse();
response.setAction( "get" );
response.setApplication( services.getApplication() );
response.setParams( ui.getQueryParameters() );
response.setProperty("maps", map);
return response;
}
@POST
@Path("{mapName}/entries")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public ApiResponse createEntries( @Context UriInfo ui,
Map<String,Object> json,
@PathParam("mapName") String mapName ) {
if ( json.size() > BATCH_SIZE_MAX ){
throw new IllegalArgumentException("Batch size "+json.size()+" is more than max size of "+BATCH_SIZE_MAX);
}
if(StringUtils.isEmpty(mapName)){
throw new IllegalArgumentException("Property mapName is required.");
}
enforceQueryFeature(ui);
final MapScope mapScope = new MapScopeImpl(services.getApplication().asId(), mapName);
final MapManagerFactory mmf = injector.getInstance(MapManagerFactory.class);
final MapManager mapManager = mmf.createMapManager(mapScope);
//TODO maybe RX flatmap this or parallel stream
json.forEach( (key, value) -> {
mapManager.putString(key, value.toString());
});
ApiResponse response = createApiResponse();
response.setAction( "post" );
response.setApplication( services.getApplication() );
response.setParams( ui.getQueryParameters() );
response.setProperty("entries", json);
return response;
}
@GET
@Path("{mapName}/keys")
@Produces(MediaType.APPLICATION_JSON)
public ApiResponse getKeys( @Context UriInfo ui,
@QueryParam("limit") int limit,
@QueryParam("cursor") String cursor,
@PathParam("mapName") String mapName ) {
if(StringUtils.isEmpty(mapName)){
throw new IllegalArgumentException("Property mapName is required.");
}
limit = validateLimit(limit);
enforceQueryFeature(ui);
final MapScope mapScope = new MapScopeImpl(services.getApplication().asId(), mapName);
final MapManagerFactory mmf = injector.getInstance(MapManagerFactory.class);
final MapManager mapManager = mmf.createMapManager(mapScope);
MapKeyResults mapKeyResults = mapManager.getKeys(cursor, limit );
ApiResponse response = createApiResponse();
response.setAction( "get" );
response.setApplication( services.getApplication() );
response.setParams( ui.getQueryParameters() );
response.setProperty("keys", mapKeyResults.getKeys());
if( StringUtils.isNotEmpty(mapKeyResults.getCursor())){
response.setProperty("cursor", mapKeyResults.getCursor());
}
return response;
}
@GET
@Path("{mapName}/entries")
@Produces(MediaType.APPLICATION_JSON)
public ApiResponse getEntries( @Context UriInfo ui,
@QueryParam("limit") int limit,
@QueryParam("cursor") String cursor,
@PathParam("mapName") String mapName ) {
if(StringUtils.isEmpty(mapName)){
throw new IllegalArgumentException("Property mapName is required.");
}
limit = validateLimit(limit);
enforceQueryFeature(ui);
final MapScope mapScope = new MapScopeImpl(services.getApplication().asId(), mapName);
final MapManagerFactory mmf = injector.getInstance(MapManagerFactory.class);
final MapManager mapManager = mmf.createMapManager(mapScope);
MapKeyResults mapKeyResults = mapManager.getKeys(cursor, limit );
Map<String, Object> results = new HashMap<>();
Observable.from(mapKeyResults.getKeys()).flatMap(key ->{
return Observable.just(results.put(key, mapManager.getString(key)));
}).toBlocking().lastOrDefault(null);
ApiResponse response = createApiResponse();
response.setAction( "get" );
response.setApplication( services.getApplication() );
response.setParams( ui.getQueryParameters() );
response.setProperty("entries", results);
if( StringUtils.isNotEmpty(mapKeyResults.getCursor())){
response.setProperty("cursor", mapKeyResults.getCursor());
}
return response;
}
@GET
@Path("{mapName}/entries/{keyName}")
@Produces(MediaType.APPLICATION_JSON)
public Object getEntry( @Context UriInfo ui,
@PathParam("mapName") String mapName,
@PathParam("keyName") String keyName,
@QueryParam("rawData") @DefaultValue("false") boolean rawData) {
enforceQueryFeature(ui);
final MapScope mapScope = new MapScopeImpl(services.getApplication().asId(), mapName);
final MapManagerFactory mmf = injector.getInstance(MapManagerFactory.class);
final MapManager mapManager = mmf.createMapManager(mapScope);
Map<String, Object> results = new HashMap<>();
Object value = mapManager.getString(keyName);
results.put(keyName, value);
if( !rawData ) {
ApiResponse response = createApiResponse();
response.setAction("get");
response.setApplication(services.getApplication());
response.setParams(ui.getQueryParameters());
response.setProperty("entries", results);
return response;
}else{
return value;
}
}
private int validateLimit(final int limit){
return limit > 0 && limit < PAGE_SIZE_MAX ? limit : PAGE_SIZE_DEFAULT;
}
private void enforceQueryFeature(UriInfo ui) {
if( StringUtils.isNotEmpty(ui.getQueryParameters().getFirst("ql"))){
throw new IllegalArgumentException("Query feature not supported for keyvaluemaps");
}
}
}