/* * Copyright © 2015 Cask Data, Inc. * * 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 co.cask.cdap.gateway.handlers; import co.cask.cdap.app.store.Store; import co.cask.cdap.common.conf.Constants; import co.cask.cdap.config.PreferencesStore; import co.cask.cdap.gateway.handlers.util.AbstractAppFabricHttpHandler; import co.cask.cdap.proto.Id; import co.cask.cdap.proto.ProgramType; import co.cask.cdap.store.NamespaceStore; import co.cask.http.HttpResponder; import com.google.gson.JsonSyntaxException; import com.google.inject.Inject; import org.jboss.netty.handler.codec.http.HttpRequest; import org.jboss.netty.handler.codec.http.HttpResponseStatus; import java.util.Map; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.QueryParam; /** * Program Preferences HTTP Handler. */ @Path(Constants.Gateway.API_VERSION_3) public class PreferencesHttpHandler extends AbstractAppFabricHttpHandler { private final PreferencesStore preferencesStore; private final Store store; private final NamespaceStore nsStore; @Inject PreferencesHttpHandler(PreferencesStore preferencesStore, Store store, NamespaceStore nsStore) { this.preferencesStore = preferencesStore; this.store = store; this.nsStore = nsStore; } //Instance Level Properties @Path("/preferences") @GET public void getInstancePrefs(HttpRequest request, HttpResponder responder) throws Exception { responder.sendJson(HttpResponseStatus.OK, preferencesStore.getProperties()); } @Path("/preferences") @DELETE public void deleteInstancePrefs(HttpRequest request, HttpResponder responder) throws Exception { preferencesStore.deleteProperties(); responder.sendStatus(HttpResponseStatus.OK); } @Path("/preferences") @PUT public void setInstancePrefs(HttpRequest request, HttpResponder responder) throws Exception { try { Map<String, String> propMap = decodeArguments(request); preferencesStore.setProperties(propMap); responder.sendStatus(HttpResponseStatus.OK); } catch (JsonSyntaxException jsonEx) { responder.sendString(HttpResponseStatus.BAD_REQUEST, "Invalid JSON in body"); } } //Namespace Level Properties //Resolved field, if set to true, returns the collapsed property map (Instance < Namespace) @Path("/namespaces/{namespace-id}/preferences") @GET public void getNamespacePrefs(HttpRequest request, HttpResponder responder, @PathParam("namespace-id") String namespace, @QueryParam("resolved") String resolved) throws Exception { if (nsStore.get(Id.Namespace.from(namespace)) == null) { responder.sendString(HttpResponseStatus.NOT_FOUND, String.format("Namespace %s not present", namespace)); } else { if (resolved != null && resolved.equals("true")) { responder.sendJson(HttpResponseStatus.OK, preferencesStore.getResolvedProperties(namespace)); } else { responder.sendJson(HttpResponseStatus.OK, preferencesStore.getProperties(namespace)); } } } @Path("/namespaces/{namespace-id}/preferences") @PUT public void setNamespacePrefs(HttpRequest request, HttpResponder responder, @PathParam("namespace-id") String namespace) throws Exception { if (nsStore.get(Id.Namespace.from(namespace)) == null) { responder.sendString(HttpResponseStatus.NOT_FOUND, String.format("Namespace %s not present", namespace)); return; } try { Map<String, String> propMap = decodeArguments(request); preferencesStore.setProperties(namespace, propMap); responder.sendStatus(HttpResponseStatus.OK); } catch (JsonSyntaxException jsonEx) { responder.sendString(HttpResponseStatus.BAD_REQUEST, "Invalid JSON in body"); } } @Path("/namespaces/{namespace-id}/preferences") @DELETE public void deleteNamespacePrefs(HttpRequest request, HttpResponder responder, @PathParam("namespace-id") String namespace) throws Exception { if (nsStore.get(Id.Namespace.from(namespace)) == null) { responder.sendString(HttpResponseStatus.NOT_FOUND, String.format("Namespace %s not present", namespace)); } else { preferencesStore.deleteProperties(namespace); responder.sendStatus(HttpResponseStatus.OK); } } //Application Level Properties //Resolved field, if set to true, returns the collapsed property map (Instance < Namespace < Application) @Path("/namespaces/{namespace-id}/apps/{application-id}/preferences") @GET public void getAppPrefs(HttpRequest request, HttpResponder responder, @PathParam("namespace-id") String namespace, @PathParam("application-id") String appId, @QueryParam("resolved") String resolved) throws Exception { if (store.getApplication(Id.Application.from(namespace, appId)) == null) { responder.sendString(HttpResponseStatus.NOT_FOUND, String.format("Application %s in Namespace %s not present", appId, namespace)); } else { if (resolved != null && resolved.equals("true")) { responder.sendJson(HttpResponseStatus.OK, preferencesStore.getResolvedProperties(namespace, appId)); } else { responder.sendJson(HttpResponseStatus.OK, preferencesStore.getProperties(namespace, appId)); } } } @Path("/namespaces/{namespace-id}/apps/{application-id}/preferences") @PUT public void putAppPrefs(HttpRequest request, HttpResponder responder, @PathParam("namespace-id") String namespace, @PathParam("application-id") String appId) throws Exception { if (store.getApplication(Id.Application.from(namespace, appId)) == null) { responder.sendString(HttpResponseStatus.NOT_FOUND, String.format("Application %s in Namespace %s not present", appId, namespace)); return; } try { Map<String, String> propMap = decodeArguments(request); preferencesStore.setProperties(namespace, appId, propMap); responder.sendStatus(HttpResponseStatus.OK); } catch (JsonSyntaxException jsonEx) { responder.sendString(HttpResponseStatus.BAD_REQUEST, "Invalid JSON in body"); } } @Path("/namespaces/{namespace-id}/apps/{application-id}/preferences") @DELETE public void deleteAppPrefs(HttpRequest request, HttpResponder responder, @PathParam("namespace-id") String namespace, @PathParam("application-id") String appId) throws Exception { if (store.getApplication(Id.Application.from(namespace, appId)) == null) { responder.sendString(HttpResponseStatus.NOT_FOUND, String.format("Application %s in Namespace %s not present", appId, namespace)); } else { preferencesStore.deleteProperties(namespace, appId); responder.sendStatus(HttpResponseStatus.OK); } } //Program Level Properties //Resolved field, if set to true, returns the collapsed property map (Instance < Namespace < Application < Program) @Path("/namespaces/{namespace-id}/apps/{application-id}/{program-type}/{program-id}/preferences") @GET public void getProgramPrefs(HttpRequest request, HttpResponder responder, @PathParam("namespace-id") String namespace, @PathParam("application-id") String appId, @PathParam("program-type") String programType, @PathParam("program-id") String programId, @QueryParam("resolved") String resolved) throws Exception { if (checkIfProgramExists(namespace, appId, programType, programId, responder)) { if (resolved != null && resolved.equals("true")) { responder.sendJson(HttpResponseStatus.OK, preferencesStore.getResolvedProperties( namespace, appId, programType, programId)); } else { responder.sendJson(HttpResponseStatus.OK, preferencesStore.getProperties( namespace, appId, programType, programId)); } } } @Path("/namespaces/{namespace-id}/apps/{application-id}/{program-type}/{program-id}/preferences") @PUT public void putProgramPrefs(HttpRequest request, HttpResponder responder, @PathParam("namespace-id") String namespace, @PathParam("application-id") String appId, @PathParam("program-type") String programType, @PathParam("program-id") String programId) throws Exception { if (checkIfProgramExists(namespace, appId, programType, programId, responder)) { try { Map<String, String> propMap = decodeArguments(request); preferencesStore.setProperties(namespace, appId, programType, programId, propMap); responder.sendStatus(HttpResponseStatus.OK); } catch (JsonSyntaxException jsonEx) { responder.sendString(HttpResponseStatus.BAD_REQUEST, "Invalid JSON in body"); } } } @Path("/namespaces/{namespace-id}/apps/{application-id}/{program-type}/{program-id}/preferences") @DELETE public void deleteProgramPrefs(HttpRequest request, HttpResponder responder, @PathParam("namespace-id") String namespace, @PathParam("application-id") String appId, @PathParam("program-type") String programType, @PathParam("program-id") String programId) throws Exception { if (checkIfProgramExists(namespace, appId, programType, programId, responder)) { preferencesStore.deleteProperties(namespace, appId, programType, programId); responder.sendStatus(HttpResponseStatus.OK); } } private boolean checkIfProgramExists(String namespace, String appId, String programType, String programId, HttpResponder responder) throws Exception { ProgramType type; try { type = ProgramType.valueOfCategoryName(programType); } catch (IllegalArgumentException e) { responder.sendString(HttpResponseStatus.BAD_REQUEST, String.format("%s is invalid ProgramType", programType)); return false; } if (!store.programExists(Id.Program.from(namespace, appId, type, programId))) { responder.sendString(HttpResponseStatus.NOT_FOUND, String.format("Program %s of Type %s in AppId %s in Namespace %s not present", programId, programType, appId, namespace)); return false; } return true; } }