/*
* 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 com.addthis.hydra.job.web.resources;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import com.addthis.codec.json.CodecJSON;
import com.addthis.hydra.job.entity.JobCommand;
import com.addthis.hydra.job.entity.JobEntityManager;
import com.addthis.hydra.job.entity.JobMacro;
import com.addthis.hydra.job.mq.HostState;
import com.addthis.hydra.job.spawn.ClientEvent;
import com.addthis.hydra.job.spawn.ClientEventListener;
import com.addthis.hydra.job.spawn.Spawn;
import com.addthis.maljson.JSONArray;
import com.addthis.maljson.JSONObject;
import com.google.common.base.Optional;
import com.sun.jersey.api.core.HttpContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Path("/update")
public class ListenResource {
private static final Logger log = LoggerFactory.getLogger(ListenResource.class);
private static final int batchInterval = Integer.parseInt(System.getProperty("spawn.batchtime", "500"));
private final Spawn spawn;
private final SystemResource systemResource;
private final AtomicInteger clientCounter;
private final int pollTimeout;
@Context
private HttpContext context;
public ListenResource(Spawn spawn, SystemResource systemResource, int pollTimeout) {
this.spawn = spawn;
this.systemResource = systemResource;
this.pollTimeout = pollTimeout;
this.clientCounter = new AtomicInteger(0);
}
@GET
@Path("/batch")
@Produces(MediaType.APPLICATION_JSON)
public Response getListenBatch(@QueryParam("timeout") Optional<Integer> timeoutParameter,
@QueryParam("batchtime") Optional<Integer> batchtimeParameter,
@QueryParam("clientId") Optional<String> clientIdParameter) {
Response response;
String clientId = (clientIdParameter.isPresent() ? clientIdParameter.get() : "noid");
int timeout = timeoutParameter.or(pollTimeout);
int batchTime = batchtimeParameter.or(batchInterval);
ClientEventListener listener = spawn.getClientEventListener(clientId);
try {
ClientEvent nextEvent = listener.events.poll(timeout, TimeUnit.MILLISECONDS);
if (nextEvent != null) {
long mark = System.currentTimeMillis();
JSONArray payload = new JSONArray();
payload.put(encodeJson(nextEvent));
for (int i = 50; i > 0; i--) {
nextEvent = listener.events.poll(batchTime, TimeUnit.MILLISECONDS);
if (nextEvent != null) {
JSONObject json = encodeJson(nextEvent);
payload.put(json);
}
if (System.currentTimeMillis() - mark > batchTime) {
break;
}
}
response = Response.ok(payload.toString()).build();
} else {
response = Response.notModified().build();
}
} catch (InterruptedException ex) {
response = Response.notModified().build();
} catch (Exception ex) {
log.warn("", ex);
response = Response.serverError().build();
}
return response;
}
private JSONObject encodeJson(ClientEvent event) throws Exception {
JSONObject json = event.toJSON();
return json;
}
@GET
@Path("/is-quiesce")
@Produces(MediaType.TEXT_PLAIN)
public Response getIsQuiesced() {
try {
return Response.ok(Boolean.toString(spawn.getSystemManager().isQuiesced())).build();
} catch (Exception ex) {
log.error("Error during construction of \"/is-quiesce\" response: ", ex);
return Response.serverError().entity(ex.getMessage()).build();
}
}
@GET
@Path("/settings")
@Produces(MediaType.APPLICATION_JSON)
public Response getSettings() {
try {
JSONObject settings = CodecJSON.encodeJSON(spawn.getSystemManager().getSettings());
return Response.ok(settings.toString()).build();
} catch (Exception ex) {
log.error("Error during construction of \"/settings\" response: " ,ex);
return Response.serverError().entity(ex.getMessage()).build();
}
}
@GET
@Path("/setup")
@Produces(MediaType.APPLICATION_JSON)
public Response getSetup() {
try {
JSONObject setup = CodecJSON.encodeJSON(spawn.getSystemManager().getSettings());
JSONArray jobs = spawn.getJobUpdateEventsSafely();
setup.put("jobs", jobs);
JSONObject macrolist = new JSONObject();
JSONObject commandlist = new JSONObject();
JSONObject hostlist = new JSONObject();
JSONObject aliases = new JSONObject();
JobEntityManager<JobMacro> jobMacroManager = spawn.getJobMacroManager();
JobEntityManager<JobCommand> jobCommandManager = spawn.getJobCommandManager();
for (String key : jobMacroManager.getKeys()) {
JobMacro macro = jobMacroManager.getEntity(key);
macrolist.put(key, macro.toJSON().put("macro", "").put("name", key));
}
for (String key : jobCommandManager.getKeys()) {
JobCommand command = jobCommandManager.getEntity(key);
commandlist.put(key, command.toJSON().put("name", key));
}
for (HostState host : spawn.hostManager.listHostStatus(null)) {
hostlist.put(host.getHostUuid(), spawn.getHostStateUpdateEvent(host));
}
for (Map.Entry<String, List<String>> alias : spawn.getAliasManager().getAliases().entrySet()) {
JSONObject aliasJson = new JSONObject();
JSONArray aliasJobs = new JSONArray();
for (String key : alias.getValue()) {
aliasJobs.put(key);
}
aliases.put(alias.getKey(), aliasJson.put("name", alias.getKey()).put("jobs", aliasJobs));
}
setup.put("macros", macrolist);
setup.put("commands", commandlist);
setup.put("hosts", hostlist);
setup.put("aliases", aliases);
setup.put("alerts", spawn.getJobAlertManager().fetchAllAlertsMap());
setup.put("spawnqueuesize", spawn.getTaskQueuedCount());
setup.put("clientId", clientCounter.incrementAndGet());
return Response.ok(setup.toString()).build();
} catch (Exception ex) {
log.error("Error during construction of \"/setup\" response: " ,ex);
return Response.serverError().entity(ex.getMessage()).build();
}
}
/** @deprecated Use {@link SystemResource#quiesceCluster(String, String, String, String)} */
@GET
@Path("/quiesce")
@Produces(MediaType.APPLICATION_JSON)
@Deprecated
public Response quiesceCluster(@QueryParam("quiesce") String quiesce,
@QueryParam("user") String user,
@QueryParam("token") String token,
@QueryParam("sudo") String sudo) {
return systemResource.quiesceCluster(quiesce, user, token, sudo);
}
/** @deprecated Use {@link SystemResource#getBalanceParams()} */
@GET
@Path("/balance.params.get")
@Produces(MediaType.APPLICATION_JSON)
@Deprecated
public Response getBalanceParams() {
return systemResource.getBalanceParams();
}
/** @deprecated Use {@link SystemResource#setBalanceParams(String)} */
@GET
@Path("/balance.params.set")
@Produces(MediaType.APPLICATION_JSON)
@Deprecated
public Response setBalanceParams(@QueryParam("params") String params) {
return systemResource.setBalanceParams(params);
}
/** @deprecated Use {@link SystemResource#setObeyTaskLimit(boolean)} */
@GET
@Path("/hostfailworker.obeyTaskLimit.set")
@Produces(MediaType.APPLICATION_JSON)
@Deprecated
public Response setObeyTaskLimit(@QueryParam("obey") boolean obey) {
return systemResource.setObeyTaskLimit(obey);
}
/** @deprecated Use {@link SystemResource#getGitProperties()} */
@GET
@Path("/git.properties")
@Produces(MediaType.APPLICATION_JSON)
@Deprecated
public Response getGitProperties() {
return systemResource.getGitProperties();
}
/** @deprecated Use {@link SystemResource#datastoreCutover(String, String, int)} */
@GET
@Path("/datastore.cutover")
@Produces(MediaType.APPLICATION_JSON)
@Deprecated
public Response datastoreCutover(@QueryParam("src") String src, @QueryParam("tar") String tar, @QueryParam("checkAll") int checkAll) {
return systemResource.datastoreCutover(src, tar, checkAll);
}
}