/* * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* * The Original Code is HAT. The Initial Developer of the * Original Code is Bill Foote, with contributions from others * at JavaSoft/Sun. */ package com.sun.tools.hat.internal.server; /** * Reads a single HTTP query from a socket, and starts up a QueryHandler * to server it. * * @author Bill Foote */ import java.net.Socket; import java.net.URLDecoder; import java.util.function.Supplier; import java.util.regex.Pattern; import java.io.UnsupportedEncodingException; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableListMultimap; import com.sun.tools.hat.internal.model.Snapshot; import com.sun.tools.hat.internal.oql.OQLEngine; public class HttpReader extends HttpHandler { private class EngineThreadLocal extends ThreadLocal<OQLEngine> { @Override protected OQLEngine initialValue() { return new OQLEngine(snapshot); } } private static class HandlerRoute { private static final Pattern SLASH = Pattern.compile("/"); private static final Pattern AMPER = Pattern.compile("[&;]"); private final String name; private final String[] parts; private final Supplier<QueryHandler> handlerFactory; public HandlerRoute(String name, Supplier<QueryHandler> handlerFactory) { this.name = name; this.parts = SLASH.split(name, -1); this.handlerFactory = handlerFactory; } private static String decode(String str) { try { return URLDecoder.decode(str, "UTF-8"); } catch (UnsupportedEncodingException exc) { // UTF-8 is always supported throw new AssertionError(exc); } } public QueryHandler parse(String queryString) { int qpos = queryString.indexOf('?'); String query = qpos == -1 ? queryString : queryString.substring(0, qpos); String[] qparts = SLASH.split(query, -1); if (qparts.length != parts.length) { return null; } StringBuilder path = new StringBuilder(); String pathInfo = null; StringBuilder urlStart = new StringBuilder(); for (int i = 0; i < parts.length; ++i) { if (parts[i].equals("*")) { pathInfo = decode(qparts[i]); } else if (parts[i].equals(qparts[i])) { path.append('/').append(parts[i]); } else { return null; } if (i > 1) { urlStart.append("../"); } } ImmutableListMultimap.Builder<String, String> params = ImmutableListMultimap.builder(); if (qpos != -1) { for (String item : AMPER.split(queryString.substring(qpos + 1))) { int epos = item.indexOf('='); if (epos != -1) { params.put(decode(item.substring(0, epos)), decode(item.substring(epos + 1))); } } } QueryHandler handler = handlerFactory.get(); handler.setPath(path.substring(2)); handler.setUrlStart(urlStart.toString()); handler.setQuery(pathInfo); handler.setParams(params.build()); return handler; } @Override public String toString() { return name; } } private final Snapshot snapshot; private final EngineThreadLocal engine = new EngineThreadLocal(); private final ImmutableList<HandlerRoute> routes = makeHandlerRoutes(); private ImmutableList<HandlerRoute> makeHandlerRoutes() { final boolean isOQLSupported = OQLEngine.isOQLSupported(); ImmutableList.Builder<HandlerRoute> builder = ImmutableList.builder(); if (isOQLSupported) { builder.add(new HandlerRoute("/oql/", () -> new OQLQuery(engine)), new HandlerRoute("/oqlhelp/", OQLHelp::new)); } builder.add(new HandlerRoute("/", () -> new AllClassesQuery(true, isOQLSupported)), new HandlerRoute("/allClassesWithPlatform/", () -> new AllClassesQuery(false, isOQLSupported)), new HandlerRoute("/showRoots/", AllRootsQuery::new), new HandlerRoute("/showInstanceCounts/", () -> new InstancesCountQuery(true)), new HandlerRoute("/showInstanceCounts/includePlatform/", () -> new InstancesCountQuery(false)), new HandlerRoute("/instances/*", () -> new InstancesQuery(false, false)), new HandlerRoute("/newInstances/*", () -> new InstancesQuery(false, true)), new HandlerRoute("/allInstances/*", () -> new InstancesQuery(true, false)), new HandlerRoute("/allNewInstances/*", () -> new InstancesQuery(true, true)), new HandlerRoute("/object/*", ObjectQuery::new), new HandlerRoute("/class/*", ClassQuery::new), new HandlerRoute("/roots/*", () -> new RootsQuery(false)), new HandlerRoute("/allRoots/*", () -> new RootsQuery(true)), new HandlerRoute("/reachableFrom/*", ReachableQuery::new), new HandlerRoute("/rootStack/*", RootStackQuery::new), new HandlerRoute("/histo/*", HistogramQuery::new), new HandlerRoute("/refsByType/*", RefsByTypeQuery::new), new HandlerRoute("/finalizerSummary/", FinalizerSummaryQuery::new), new HandlerRoute("/finalizerObjects/", FinalizerObjectsQuery::new), new HandlerRoute("/debug/*", DebugQuery::new)); return builder.build(); } public HttpReader (Socket s, Snapshot snapshot) { super(s); this.snapshot = snapshot; } @Override protected QueryHandler requestHandler(String query) { if (snapshot == null) { return new ErrorQuery("The heap snapshot is still being read."); } QueryHandler handler = null; for (HandlerRoute route : routes) { handler = route.parse(query); if (handler != null) { break; } } if (handler != null) { handler.setSnapshot(snapshot); } return handler; } }