Java tutorial
/* * Copyright 2010, 2014 Attribyte, LLC * * 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 org.attribyte.api.pubsub.impl.server.admin; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.common.primitives.Longs; import org.attribyte.api.Logger; import org.attribyte.api.http.AuthScheme; import org.attribyte.api.http.impl.BasicAuthScheme; import org.attribyte.api.pubsub.CallbackMetrics; import org.attribyte.api.pubsub.HostCallbackMetrics; import org.attribyte.api.pubsub.HubDatastore; import org.attribyte.api.pubsub.HubEndpoint; import org.attribyte.api.pubsub.NotificationMetrics; import org.attribyte.api.pubsub.Subscriber; import org.attribyte.api.pubsub.Subscription; import org.attribyte.api.pubsub.Topic; import org.attribyte.api.pubsub.impl.server.util.NotificationRecord; import org.attribyte.api.pubsub.impl.server.admin.model.DisplayCallbackMetrics; import org.attribyte.api.pubsub.impl.server.admin.model.DisplayCallbackMetricsDetail; import org.attribyte.api.pubsub.impl.server.admin.model.DisplayNotificationMetrics; import org.attribyte.api.pubsub.impl.server.admin.model.DisplayNotificationMetricsDetail; import org.attribyte.api.pubsub.impl.server.admin.model.DisplaySubscribedHost; import org.attribyte.api.pubsub.impl.server.admin.model.DisplayTopic; import org.attribyte.api.pubsub.impl.server.admin.model.Paging; import org.attribyte.api.pubsub.impl.server.util.Invalidatable; import org.attribyte.api.pubsub.impl.server.util.SubscriptionEvent; import org.attribyte.api.pubsub.impl.server.util.SubscriptionRequestRecord; import org.attribyte.util.URIEncoder; import org.stringtemplate.v4.DateRenderer; import org.stringtemplate.v4.ST; import org.stringtemplate.v4.STErrorListener; import org.stringtemplate.v4.STGroup; import org.stringtemplate.v4.STGroupDir; import org.stringtemplate.v4.STGroupFile; import org.stringtemplate.v4.misc.STMessage; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; import java.net.InetAddress; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import static org.attribyte.api.pubsub.impl.server.util.ServerUtil.splitPath; @SuppressWarnings("serial") /** * Implements the browser-based administrative console. */ public class AdminServlet extends HttpServlet { public AdminServlet(final HubEndpoint endpoint, final Collection<Invalidatable> invalidateItems, final SubscriptionRequestRecord.Source subscriptionRequestsSource, final NotificationRecord.Source notificationSource, final AdminAuth auth, final String templateDirectory, final Logger logger) { this.endpoint = endpoint; this.invalidateItems = ImmutableList.copyOf(invalidateItems); this.subscriptionRequestsSource = subscriptionRequestsSource; this.notificationSource = notificationSource; this.datastore = endpoint.getDatastore(); this.auth = auth; this.templateGroup = new STGroupDir(templateDirectory, '$', '$'); this.templateGroup.setListener(new ErrorListener()); this.templateGroup.registerRenderer(java.util.Date.class, new DateRenderer()); File globalConstantsFile = new File(templateDirectory, "constants.stg"); STGroupFile globalConstants; if (globalConstantsFile.exists()) { globalConstants = new STGroupFile(globalConstantsFile.getAbsolutePath()); this.templateGroup.importTemplates(globalConstants); } this.logger = logger; } @Override protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws IOException { if (!auth.authIsValid(request, response)) return; List<String> path = splitPath(request); String obj = path.size() > 0 ? path.get(0) : null; if (obj != null) { if (obj.equals("cache")) { endpoint.invalidateCaches(); invalidateItems.forEach(Invalidatable::invalidate); response.setStatus(HttpServletResponse.SC_OK); } else { sendNotFound(response); } } else { sendNotFound(response); } } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { if (!auth.authIsValid(request, response)) return; List<String> path = splitPath(request); String obj = path.size() > 0 ? path.get(0) : null; if (obj != null) { switch (obj) { case "subscription": postSubscriptionEdit(request, path.size() > 1 ? path.get(1) : null, response); break; case "topic": postTopicAdd(request, response); break; default: sendNotFound(response); break; } } else { sendNotFound(response); } } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { if (!auth.authIsValid(request, response)) return; List<String> path = splitPath(request); String obj = path.size() > 0 ? path.get(0) : null; if (obj == null || obj.equals("topics")) { boolean activeOnly = obj == null || path.size() > 1 && path.get(1).equals("active"); renderTopics(request, activeOnly, response); } else if (obj.equals("subscribers")) { renderSubscribers(request, response); } else if (obj.equals("topic")) { if (path.size() > 1) { boolean activeOnly = path.size() > 2 && path.get(2).equals("active"); renderTopicSubscriptions(path.get(1), request, activeOnly, response); } else { sendNotFound(response); } } else if (obj.equals("host")) { if (path.size() > 1) { boolean activeOnly = path.size() > 2 && path.get(2).equals("active"); renderHostSubscriptions(path.get(1), request, activeOnly, response); } else { sendNotFound(response); } } else if (obj.equals("subscriptions")) { boolean activeOnly = path.size() > 1 && path.get(1).equals("active"); renderAllSubscriptions(request, activeOnly, response); } else if (obj.equals("metrics")) { if (path.size() > 1) { renderCallbackMetricsDetail(path.get(1), response); } else { renderCallbackMetrics(response); } } else if (obj.equals("nmetrics")) { if (path.size() > 1) { long topicId = Long.parseLong(path.get(1)); renderNotificationMetricsDetail(topicId, response); } else { renderNotificationMetrics(response); } } else if (obj.equals("notifications")) { renderNotifications(response); } else if (obj.equals("subscription_events")) { renderSubscriptionEvents(response); } else { sendNotFound(response); } } private void renderSubscribers(final HttpServletRequest request, final HttpServletResponse response) throws IOException { ST mainTemplate = getTemplate("main"); ST subscriberTemplate = getTemplate("subscribers"); if (mainTemplate == null || subscriberTemplate == null) { response.sendError(500, "Missing templates"); return; } try { Paging paging = getPaging(request); List<DisplaySubscribedHost> subscribers = Lists.newArrayListWithExpectedSize(25); List<String> endpoints = datastore.getSubscribedHosts(paging.getStart(), pageRequestSize); paging = nextPaging(paging, endpoints); for (String host : endpoints) { subscribers.add(new DisplaySubscribedHost(host, datastore.countActiveHostSubscriptions(host))); } subscriberTemplate.add("subscribers", subscribers); subscriberTemplate.add("paging", paging); mainTemplate.add("content", subscriberTemplate.render()); response.setContentType("text/html"); response.getWriter().print(mainTemplate.render()); response.getWriter().flush(); } catch (Exception se) { se.printStackTrace(); response.sendError(500, "Datastore error"); } } private void renderTopics(final HttpServletRequest request, boolean activeOnly, final HttpServletResponse response) throws IOException { ST mainTemplate = getTemplate("main"); ST subscriberTemplate = getTemplate("topics"); if (mainTemplate == null || subscriberTemplate == null) { response.sendError(500, "Missing templates"); return; } try { Paging paging = getPaging(request); List<DisplayTopic> displayTopics = Lists.newArrayListWithExpectedSize(25); List<Topic> topics = activeOnly ? datastore.getActiveTopics(paging.getStart(), pageRequestSize) : datastore.getTopics(paging.getStart(), pageRequestSize); paging = nextPaging(paging, topics); for (Topic topic : topics) { displayTopics.add(new DisplayTopic(topic, datastore.countActiveSubscriptions(topic.getId()), new DisplayNotificationMetrics(topic, endpoint.getNotificationMetrics(topic.getId())))); } subscriberTemplate.add("topics", displayTopics); subscriberTemplate.add("activeOnly", activeOnly); subscriberTemplate.add("paging", paging); mainTemplate.add("content", subscriberTemplate.render()); response.setContentType("text/html"); response.getWriter().print(mainTemplate.render()); response.getWriter().flush(); } catch (Exception se) { se.printStackTrace(); response.sendError(500, "Datastore error"); } } private Set<Subscription.Status> getSubscriptionStatus(HttpServletRequest request, final boolean activeOnly) { if (activeOnly) { return Collections.singleton(Subscription.Status.ACTIVE); } String[] status = request.getParameterValues("status"); if (status == null || status.length == 0) { return Collections.emptySet(); } else { Set<Subscription.Status> statusSet = Sets.newHashSetWithExpectedSize(4); for (String s : status) { Subscription.Status toAdd = Subscription.Status.valueOf(s); if (toAdd != Subscription.Status.INVALID) { statusSet.add(toAdd); } } return statusSet; } } private void renderTopicSubscriptions(final String topicIdStr, final HttpServletRequest request, final boolean activeOnly, final HttpServletResponse response) throws IOException { ST mainTemplate = getTemplate("main"); ST subscriptionsTemplate = getTemplate("topic_subscriptions"); if (mainTemplate == null || subscriptionsTemplate == null) { response.sendError(500, "Missing templates"); return; } try { long topicId = Long.parseLong(topicIdStr); Topic topic = datastore.getTopic(topicId); if (topic == null) { sendNotFound(response); return; } Paging paging = getPaging(request); List<Subscription> subscriptions = datastore.getTopicSubscriptions(topic, getSubscriptionStatus(request, activeOnly), paging.getStart(), pageRequestSize); paging = nextPaging(paging, subscriptions); subscriptionsTemplate.add("subscriptions", subscriptions); subscriptionsTemplate.add("topic", new DisplayTopic(topic, 0, null)); subscriptionsTemplate.add("activeOnly", activeOnly); subscriptionsTemplate.add("paging", paging); mainTemplate.add("content", subscriptionsTemplate.render()); response.setContentType("text/html"); response.getWriter().print(mainTemplate.render()); response.getWriter().flush(); } catch (NumberFormatException nfe) { response.sendError(400, "Invalid topic id"); } catch (Exception se) { se.printStackTrace(); response.sendError(500, "Datastore error"); } } private void renderHostSubscriptions(final String host, final HttpServletRequest request, final boolean activeOnly, final HttpServletResponse response) throws IOException { ST mainTemplate = getTemplate("main"); ST subscriptionsTemplate = getTemplate("host_subscriptions"); if (mainTemplate == null || subscriptionsTemplate == null) { response.sendError(500, "Missing templates"); return; } try { Paging paging = getPaging(request); List<Subscription> subscriptions = datastore.getHostSubscriptions(host, getSubscriptionStatus(request, activeOnly), paging.getStart(), pageRequestSize); paging = nextPaging(paging, subscriptions); subscriptionsTemplate.add("subscriptions", subscriptions); subscriptionsTemplate.add("host", host); subscriptionsTemplate.add("activeOnly", activeOnly); subscriptionsTemplate.add("paging", paging); mainTemplate.add("content", subscriptionsTemplate.render()); response.setContentType("text/html"); response.getWriter().print(mainTemplate.render()); response.getWriter().flush(); } catch (NumberFormatException nfe) { response.sendError(400, "Invalid topic id"); } catch (Exception se) { se.printStackTrace(); response.sendError(500, "Datastore error"); } } private void renderAllSubscriptions(final HttpServletRequest request, final boolean activeOnly, final HttpServletResponse response) throws IOException { ST mainTemplate = getTemplate("main"); ST subscriptionsTemplate = getTemplate("all_subscriptions"); if (mainTemplate == null || subscriptionsTemplate == null) { response.sendError(500, "Missing templates"); return; } try { Paging paging = getPaging(request); List<Subscription> subscriptions = datastore.getSubscriptions( getSubscriptionStatus(request, activeOnly), paging.getStart(), pageRequestSize); paging = nextPaging(paging, subscriptions); subscriptionsTemplate.add("subscriptions", subscriptions); subscriptionsTemplate.add("activeOnly", activeOnly); subscriptionsTemplate.add("paging", paging); mainTemplate.add("content", subscriptionsTemplate.render()); response.setContentType("text/html"); response.getWriter().print(mainTemplate.render()); response.getWriter().flush(); } catch (NumberFormatException nfe) { response.sendError(400, "Invalid topic id"); } catch (IOException ioe) { throw ioe; } catch (Exception se) { se.printStackTrace(); response.sendError(500, "Datastore error"); } } private Paging getPaging(final HttpServletRequest request) { String pageStr = request.getParameter("p"); if (pageStr == null || pageStr.trim().length() == 0) { pageStr = "1"; } return new Paging(Integer.parseInt(pageStr), maxPerPage, false); } /** * Expects the input list to have at least one additional object that is * used to determine if there are more pages. * @param currPage The current page. * @param pageList The list of objects. * @return The new paging. */ private Paging nextPaging(final Paging currPage, final List<?> pageList) { if (pageList.size() > maxPerPage) { while (pageList.size() > maxPerPage) pageList.remove(pageList.size() - 1); return new Paging(currPage.getCurr(), maxPerPage, true); } else { return new Paging(currPage.getCurr(), maxPerPage, false); } } private void renderCallbackMetrics(final HttpServletResponse response) throws IOException { ST mainTemplate = getTemplate("main"); ST metricsTemplate = getTemplate("callback_metrics"); if (mainTemplate == null || metricsTemplate == null) { response.sendError(500, "Missing templates"); return; } try { CallbackMetrics globalMetrics = endpoint.getGlobalCallbackMetrics(); List<HostCallbackMetrics> metrics = endpoint .getHostCallbackMetrics(CallbackMetrics.Sort.THROUGHPUT_DESC, 25); List<DisplayCallbackMetrics> displayMetrics = Lists.newArrayListWithCapacity(metrics.size() + 1); displayMetrics.add(new DisplayCallbackMetrics("[all]", globalMetrics)); displayMetrics.addAll(metrics.stream().map(hcm -> new DisplayCallbackMetrics(hcm.host, hcm)) .collect(Collectors.toList())); metricsTemplate.add("metrics", displayMetrics); mainTemplate.add("content", metricsTemplate.render()); response.setContentType("text/html"); response.getWriter().print(mainTemplate.render()); response.getWriter().flush(); } catch (IOException ioe) { throw ioe; } catch (Exception se) { se.printStackTrace(); response.sendError(500, "Datastore error"); } } private void renderCallbackMetricsDetail(final String hostOrId, final HttpServletResponse response) throws IOException { ST mainTemplate = getTemplate("main"); ST metricsTemplate = getTemplate("metrics_detail"); if (mainTemplate == null || metricsTemplate == null) { response.sendError(500, "Missing templates"); return; } try { final CallbackMetrics detailMetrics; final String title; final String host; final long subscriptionId; Long maybeSubscriptionId = hostOrId == null ? null : Longs.tryParse(hostOrId); if (maybeSubscriptionId != null) { subscriptionId = maybeSubscriptionId; host = null; } else { subscriptionId = 0L; host = hostOrId; } if (subscriptionId > 0) { Subscription subscription = datastore.getSubscription(subscriptionId); if (subscription != null) { title = subscription.getCallbackURL(); detailMetrics = endpoint.getSubscriptionCallbackMetrics(subscriptionId); } else { sendNotFound(response); return; } } else if (host == null || host.equalsIgnoreCase("[all]")) { title = "All Hosts"; detailMetrics = endpoint.getGlobalCallbackMetrics(); } else { title = host; detailMetrics = endpoint.getHostCallbackMetrics(host); } metricsTemplate.add("metrics", new DisplayCallbackMetricsDetail(title, detailMetrics)); mainTemplate.add("content", metricsTemplate.render()); response.setContentType("text/html"); response.getWriter().print(mainTemplate.render()); response.getWriter().flush(); } catch (IOException ioe) { throw ioe; } catch (Exception se) { se.printStackTrace(); response.sendError(500, "Datastore error"); } } private void renderNotificationMetrics(final HttpServletResponse response) throws IOException { ST mainTemplate = getTemplate("main"); ST metricsTemplate = getTemplate("notification_metrics"); if (mainTemplate == null || metricsTemplate == null) { response.sendError(500, "Missing templates"); return; } try { NotificationMetrics globalMetrics = endpoint.getGlobalNotificationMetrics(); List<NotificationMetrics> metrics = endpoint .getNotificationMetrics(NotificationMetrics.Sort.THROUGHPUT_DESC, 25); List<DisplayNotificationMetrics> displayMetrics = Lists.newArrayListWithCapacity(metrics.size() + 1); displayMetrics.add(new DisplayNotificationMetrics(null, globalMetrics)); for (NotificationMetrics topicMetrics : metrics) { Topic topic = datastore.getTopic(topicMetrics.topicId); displayMetrics.add(new DisplayNotificationMetrics(topic, topicMetrics)); } metricsTemplate.add("metrics", displayMetrics); mainTemplate.add("content", metricsTemplate.render()); response.setContentType("text/html"); response.getWriter().print(mainTemplate.render()); response.getWriter().flush(); } catch (IOException ioe) { throw ioe; } catch (Exception se) { se.printStackTrace(); response.sendError(500, "Datastore error"); } } private void renderNotificationMetricsDetail(final long topicId, final HttpServletResponse response) throws IOException { ST mainTemplate = getTemplate("main"); ST metricsTemplate = getTemplate("notification_metrics_detail"); if (mainTemplate == null || metricsTemplate == null) { response.sendError(500, "Missing templates"); return; } try { final NotificationMetrics detailMetrics; final String title; if (topicId > 0) { Topic topic = datastore.getTopic(topicId); if (topic != null) { title = topic.getURL(); detailMetrics = endpoint.getNotificationMetrics(topicId); } else { sendNotFound(response); return; } } else { title = "[all]"; detailMetrics = endpoint.getGlobalNotificationMetrics(); } metricsTemplate.add("metrics", new DisplayNotificationMetricsDetail(title, detailMetrics)); mainTemplate.add("content", metricsTemplate.render()); response.setContentType("text/html"); response.getWriter().print(mainTemplate.render()); response.getWriter().flush(); } catch (IOException ioe) { throw ioe; } catch (Exception se) { se.printStackTrace(); response.sendError(500, "Datastore error"); } } private void renderSubscriptionEvents(final HttpServletResponse response) throws IOException { ST mainTemplate = getTemplate("main"); ST notificationsTemplate = getTemplate("latest_subscription_events"); if (mainTemplate == null || notificationsTemplate == null) { response.sendError(500, "Missing templates"); return; } try { List<SubscriptionEvent> subscriptionRequestRecords = subscriptionRequestsSource.latestEvents(100); notificationsTemplate.add("events", subscriptionRequestRecords); mainTemplate.add("content", notificationsTemplate.render()); response.setContentType("text/html"); response.getWriter().print(mainTemplate.render()); response.getWriter().flush(); } catch (Exception se) { se.printStackTrace(); response.sendError(500, "Template error"); } } private void renderNotifications(final HttpServletResponse response) throws IOException { ST mainTemplate = getTemplate("main"); ST notificationsTemplate = getTemplate("latest_notifications"); if (mainTemplate == null || notificationsTemplate == null) { response.sendError(500, "Missing templates"); return; } try { List<NotificationRecord> notificationRecords = notificationSource.latestNotifications(100); notificationsTemplate.add("notifications", notificationRecords); mainTemplate.add("content", notificationsTemplate.render()); response.setContentType("text/html"); response.getWriter().print(mainTemplate.render()); response.getWriter().flush(); } catch (Exception se) { se.printStackTrace(); response.sendError(500, "Template error"); } } private void postTopicAdd(final HttpServletRequest request, final HttpServletResponse response) throws IOException { String url = request.getParameter("url"); if (url == null || url.trim().length() == 0) { response.sendError(400); return; } try { Topic topic = datastore.getTopic(url.trim(), false); if (topic != null) { //Exists response.setStatus(200); response.getWriter().println("false"); } else { datastore.getTopic(url.trim(), true); //Created response.setStatus(201); response.getWriter().println("true"); } } catch (Exception se) { logger.error("Problem adding topic", se); response.sendError(500); } } private final Map<String, Integer> extendLeaseValues = ImmutableMap.of("hour", 3600, "day", 24 * 3600, "week", 24 * 7 * 3600, "month", 24 * 7 * 30 * 3600, "never", Integer.MAX_VALUE); private int translateExtendLease(final String extendLease) { Integer extendLeaseSeconds = extendLeaseValues.get(extendLease != null ? extendLease : "week"); return extendLeaseSeconds != null ? extendLeaseSeconds : extendLeaseValues.get("week"); } private void postSubscriptionEdit(final HttpServletRequest request, final String idStr, final HttpServletResponse response) throws IOException { if (idStr == null || idStr.trim().length() == 0) { postSubscriptionAdd(request, response); return; } String action = request.getParameter("op"); //Expect: 'enable', disable', 'expire', 'extend' int extendLeaseSeconds = translateExtendLease(request.getParameter("extendLease")); try { long id = Long.parseLong(idStr); Subscription subscription = datastore.getSubscription(id); if (subscription != null) { Subscriber currSubscriber = datastore.getSubscriber(subscription.getEndpointId()); if (currSubscriber == null) { response.sendError(500, "Data integrity violation"); return; } String callbackUsername = Strings.nullToEmpty(request.getParameter("callbackUsername")).trim(); String callbackPassword = Strings.nullToEmpty(request.getParameter("callbackPassword")).trim(); Subscriber newSubscriber; if (callbackUsername.length() > 0 && callbackPassword.length() > 0) { AuthScheme authScheme = datastore.resolveAuthScheme("Basic"); String authId = getBasicAuthId(callbackUsername, callbackPassword); newSubscriber = datastore.getSubscriber(currSubscriber.getURL(), authScheme, authId, true); if (newSubscriber.getId() != currSubscriber.getId()) { //Change the endpoint... subscription = new Subscription.Builder(subscription, newSubscriber).create(); datastore.updateSubscription(subscription, false); } } if (action == null || action.length() == 0) { response.getWriter().print(subscription.getStatus().toString()); response.setStatus(200); } else if (action.equals("enable")) { if (!subscription.isActive()) { datastore.changeSubscriptionStatus(id, Subscription.Status.ACTIVE, extendLeaseSeconds); } response.setStatus(200); response.getWriter().print("ACTIVE"); } else if (action.equals("disable")) { if (!subscription.isRemoved()) { datastore.changeSubscriptionStatus(id, Subscription.Status.REMOVED, 0); } response.setStatus(200); response.getWriter().print("REMOVED"); } else if (action.equals("expire")) { if (!subscription.isExpired()) { datastore.expireSubscription(id); } response.setStatus(200); response.getWriter().print("EXPIRED"); } else if (action.equals("extend")) { datastore.changeSubscriptionStatus(id, Subscription.Status.ACTIVE, extendLeaseSeconds); response.getWriter().print("ACTIVE"); response.setStatus(200); } else { response.getWriter().print(subscription.getStatus().toString()); response.setStatus(200); } } else { sendNotFound(response); } } catch (IOException ioe) { throw ioe; } catch (Exception se) { logger.error("Problem editing subscription", se); response.sendError(500); } } private final URIEncoder urlEncoder = new URIEncoder(); private void postSubscriptionAdd(final HttpServletRequest request, final HttpServletResponse response) throws IOException { try { String topicURL = request.getParameter("topicURL"); if (topicURL == null || topicURL.length() == 0) { response.sendError(400, "The 'topicURL' must be specified"); return; } try { topicURL = urlEncoder.recode(topicURL); } catch (Exception e) { response.sendError(400, "The 'topicURL' is invalid"); return; } String callbackURL = request.getParameter("callbackURL"); if (callbackURL == null || callbackURL.length() == 0) { response.sendError(400, "The 'callbackURL' must be specified"); return; } try { callbackURL = urlEncoder.recode(callbackURL); } catch (Exception e) { response.sendError(400, "The 'callbackURL' is invalid"); return; } Subscription existingSubscription = datastore.getSubscription(topicURL, callbackURL); if (existingSubscription != null) { //Nope...editing.. response.setStatus(200); response.getWriter().print(existingSubscription.getId()); return; } Subscription.Status status = Subscription.Status.REMOVED; String statusStr = request.getParameter("status"); if (statusStr.equals("active")) { status = Subscription.Status.ACTIVE; } else if (statusStr.equals("expired")) { status = Subscription.Status.EXPIRED; } String callbackHostURL = org.attribyte.api.http.Request.getHostURL(callbackURL); String callbackAuthScheme = request.getParameter("callbackAuthScheme"); String authId = null; AuthScheme authScheme = null; if (callbackAuthScheme != null && callbackAuthScheme.equalsIgnoreCase("basic")) { String callbackUsername = Strings.nullToEmpty(request.getParameter("callbackUsername")); String callbackPassword = Strings.nullToEmpty(request.getParameter("callbackPassword")); if (callbackUsername.length() > 0 && callbackPassword.length() > 0) { authScheme = datastore.resolveAuthScheme("Basic"); authId = getBasicAuthId(callbackUsername, callbackPassword); } } else { response.sendError(400, "Only 'Basic' auth is currently supported"); return; } int extendLeaseSeconds = translateExtendLease(request.getParameter("extendLease")); String hubSecret = request.getParameter("hubSecret"); Subscriber subscriber = datastore.getSubscriber(callbackHostURL, authScheme, authId, true); //Create, if required. Topic topic = datastore.getTopic(topicURL, true); Subscription.Builder builder = new Subscription.Builder(0L, callbackURL, topic, subscriber); builder.setStatus(status); builder.setLeaseSeconds(extendLeaseSeconds); builder.setSecret(hubSecret); datastore.updateSubscription(builder.create(), status == Subscription.Status.ACTIVE); //Updated... response.setStatus(201); response.getWriter().print("ok"); } catch (IOException ioe) { throw ioe; } catch (Exception se) { logger.error("Problem creating subscription", se); response.sendError(500); } } private void sendNotFound(HttpServletResponse response) throws IOException { response.sendError(404, "Not Found"); } /** * Gets the endpoint basic authId. * @param username The username. * @param password The password. * @return The auth id. */ private String getBasicAuthId(final String username, final String password) { return BasicAuthScheme.buildAuthHeaderValue(username, password).substring("Basic ".length()).trim(); } /** * Gets a template instance. * @param name The template name. * @return The instance. */ private ST getTemplate(final String name) { try { if (debug) templateGroup.unload(); ST template = templateGroup.getInstanceOf(name); if (template != null && name.equals("main")) { //Add metadata... template.add("hostname", InetAddress.getLocalHost().getHostName()); template.add("time", new Date()); } return template; } catch (Exception e) { e.printStackTrace(); return null; } } static final class ErrorListener implements STErrorListener { public void compileTimeError(STMessage msg) { System.out.println(msg); } public void runTimeError(STMessage msg) { System.out.println(msg); } public void IOError(STMessage msg) { System.out.println(msg); } public void internalError(STMessage msg) { System.out.println(msg); } } private final HubDatastore datastore; private final ImmutableList<Invalidatable> invalidateItems; private final NotificationRecord.Source notificationSource; private final SubscriptionRequestRecord.Source subscriptionRequestsSource; private final HubEndpoint endpoint; private final Logger logger; private final AdminAuth auth; private final STGroup templateGroup; private final boolean debug = true; private final int maxPerPage = 10; private final int pageRequestSize = maxPerPage + 1; }