src/java.management.rest/share/classes/javax/management/remote/rest/JmxRestAdapterImpl.java
author hb
Tue, 29 Aug 2017 13:34:15 +0530
branchjmx-rest-api
changeset 55985 0c5a02edfdef
permissions -rw-r--r--
REST Adapter Initial commit 1. Unit tested and working GET/POST interfaces 2. Unit tested and working JSON parser
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
55985
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
     1
/*
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
     2
 * To change this license header, choose License Headers in Project Properties.
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
     3
 * To change this template file, choose Tools | Templates
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
     4
 * and open the template in the editor.
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
     5
 */
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
     6
package javax.management.remote.rest;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
     7
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
     8
import com.oracle.jmx.remote.rest.http.PostRequestHandler;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
     9
import com.oracle.jmx.remote.rest.http.GetRequestHandler;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    10
import com.sun.jmx.remote.security.JMXPluggableAuthenticator;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    11
import com.sun.jmx.remote.security.JMXSubjectDomainCombiner;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    12
import com.sun.jmx.remote.security.SubjectDelegator;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    13
import com.sun.net.httpserver.*;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    14
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    15
import javax.management.*;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    16
import javax.management.relation.MBeanServerNotificationFilter;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    17
import javax.management.remote.JMXAuthenticator;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    18
import com.oracle.jmx.remote.rest.json.JSONArray;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    19
import com.oracle.jmx.remote.rest.json.JSONObject;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    20
import com.oracle.jmx.remote.rest.json.JSONPrimitive;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    21
import com.oracle.jmx.remote.rest.mapper.JSONMappingFactory;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    22
import javax.security.auth.Subject;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    23
import java.io.*;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    24
import java.lang.reflect.InvocationHandler;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    25
import java.lang.reflect.Method;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    26
import java.lang.reflect.Proxy;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    27
import java.net.*;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    28
import java.security.AccessControlContext;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    29
import java.security.AccessController;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    30
import java.security.PrivilegedAction;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    31
import java.util.*;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    32
import com.sun.net.httpserver.Authenticator;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    33
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    34
/**
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    35
 * @author harsha
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    36
 */
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    37
public final class JmxRestAdapterImpl implements JmxRestAdapter, NotificationListener {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    38
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    39
    // TODO: These should be wrapped in ReadWriteLock
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    40
    private static final Map<String, MBeanServer> authMBeanServer = new HashMap<>();
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    41
    final List<String> allowedMbeans = new ArrayList<>();
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    42
    private final HttpServer httpServer;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    43
    private final String contextStr;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    44
    private final Map<String, ?> env;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    45
    private final MBeanServer mbeanServer;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    46
    private final String realm = "";
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    47
    private final GetRequestHandler getHandler;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    48
    private final PostRequestHandler postHandler;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    49
    private HttpContext httpContext;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    50
    private JMXAuthenticator authenticator = null;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    51
    private boolean started = false;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    52
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    53
    JmxRestAdapterImpl(HttpServer hServer, String context, Map<String, ?> env, MBeanServer mbeanServer) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    54
        httpServer = hServer;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    55
        this.contextStr = context;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    56
        this.env = env;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    57
        this.mbeanServer = mbeanServer;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    58
        if (env.get("jmx.remote.x.authentication") != null) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    59
            authenticator = (JMXAuthenticator) env.get(JmxRestAdapterImpl.AUTHENTICATOR);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    60
            if (authenticator == null) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    61
                if (env.get("jmx.remote.x.password.file") != null
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    62
                        || env.get("jmx.remote.x.login.config") != null) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    63
                    authenticator = new JMXPluggableAuthenticator(env);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    64
                } else {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    65
                    // Throw exception for invalid authentication config
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    66
                }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    67
            }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    68
        }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    69
        introspectMBeanTypes(mbeanServer);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    70
        MBeanServerNotificationFilter filter = new MBeanServerNotificationFilter();
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    71
        filter.enableAllObjectNames();
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    72
        try {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    73
            mbeanServer.addNotificationListener(MBeanServerDelegate.DELEGATE_NAME, this, filter, null);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    74
        } catch (InstanceNotFoundException ex) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    75
        }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    76
        getHandler = new GetRequestHandler(mbeanServer, allowedMbeans);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    77
        postHandler = new PostRequestHandler(mbeanServer, allowedMbeans);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    78
    }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    79
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    80
    @Override
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    81
    public synchronized void start() {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    82
        if (!started) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    83
            httpContext = httpServer.createContext("/jmx/" + contextStr + "/", new DefaultHTTPHandler());
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    84
            if (env.get("jmx.remote.x.authentication") != null) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    85
                httpContext.setAuthenticator(new BasicAuthenticator());
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    86
            }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    87
            started = true;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    88
        }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    89
    }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    90
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    91
    @Override
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    92
    public synchronized void stop() {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    93
        if (!started) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    94
            throw new IllegalStateException("Rest Adapter not started yet");
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    95
        }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    96
        httpServer.removeContext(httpContext);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    97
        started = false;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    98
    }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
    99
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   100
    @Override
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   101
    public String getBaseUrl() {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   102
        if (!started) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   103
            throw new IllegalStateException("Adapter not started");
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   104
        }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   105
        try {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   106
            if (httpServer instanceof HttpsServer) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   107
                return "https://" + InetAddress.getLocalHost().getHostName() + ":" + httpServer.getAddress().getPort() + "/jmx/" + contextStr + "/";
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   108
            }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   109
            return "http://" + InetAddress.getLocalHost().getHostName() + ":" + httpServer.getAddress().getPort() + "/jmx/" + contextStr + "/";
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   110
        } catch (UnknownHostException ex) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   111
            return "http://localhost" + ":" + httpServer.getAddress().getPort() + "/jmx/" + contextStr + "/";
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   112
        }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   113
    }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   114
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   115
    @Override
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   116
    public MBeanServer getMBeanServer() {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   117
        return mbeanServer;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   118
    }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   119
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   120
    private boolean isMBeanAllowed(ObjectName objName) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   121
        try {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   122
            MBeanInfo mInfo = mbeanServer.getMBeanInfo(objName);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   123
            MBeanAttributeInfo[] attrsInfo = mInfo.getAttributes();
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   124
            for (MBeanAttributeInfo attrInfo : attrsInfo) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   125
                String type = attrInfo.getType();
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   126
                if (!JSONMappingFactory.INSTANCE.isTypeMapped(type)) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   127
                    return false;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   128
                }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   129
            }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   130
            MBeanOperationInfo[] operations = mInfo.getOperations();
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   131
            for (MBeanOperationInfo opInfo : operations) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   132
                MBeanParameterInfo[] signature = opInfo.getSignature();
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   133
                for (MBeanParameterInfo sig : signature) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   134
                    if (!JSONMappingFactory.INSTANCE.isTypeMapped(sig.getType())) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   135
                        return false;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   136
                    }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   137
                }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   138
                if (!JSONMappingFactory.INSTANCE.isTypeMapped(opInfo.getReturnType())) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   139
                    return false;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   140
                }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   141
            }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   142
            return true;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   143
        } catch (InstanceNotFoundException | IntrospectionException | ReflectionException | ClassNotFoundException ex) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   144
            ex.printStackTrace();
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   145
            return false;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   146
        }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   147
    }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   148
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   149
    private void introspectMBeanTypes(MBeanServer server) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   150
        if (allowedMbeans.isEmpty()) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   151
            Set<ObjectInstance> allMBeans = server.queryMBeans(null, null); // get all Mbeans
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   152
            allMBeans.stream().filter((objIns) -> (isMBeanAllowed(objIns.getObjectName())))
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   153
                    .forEachOrdered(objIns -> allowedMbeans.add(objIns.getObjectName().toString()));
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   154
        }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   155
    }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   156
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   157
    @Override
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   158
    public void handleNotification(Notification notification, Object handback) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   159
        try {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   160
            MBeanServerNotification mbs = (MBeanServerNotification) notification;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   161
            if (MBeanServerNotification.REGISTRATION_NOTIFICATION.equals(mbs.getType())) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   162
                ObjectName mBeanName = mbs.getMBeanName();
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   163
                if (isMBeanAllowed(mBeanName)) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   164
                    allowedMbeans.add(mBeanName.toString());
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   165
                }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   166
            } else if (MBeanServerNotification.UNREGISTRATION_NOTIFICATION.equals(mbs.getType())) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   167
                if (allowedMbeans.contains(mbs.getMBeanName().toString())) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   168
                    allowedMbeans.remove(mbs.getMBeanName().toString());
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   169
                }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   170
            }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   171
        } catch (Exception e) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   172
        }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   173
    }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   174
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   175
    private MBeanServer getMBeanServerProxy(MBeanServer mbeaServer, Subject subject) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   176
        return (MBeanServer) Proxy.newProxyInstance(MBeanServer.class.getClassLoader(),
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   177
                new Class<?>[]{MBeanServer.class},
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   178
                new AuthInvocationHandler(mbeaServer, subject));
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   179
    }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   180
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   181
    private static class HttpUtil {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   182
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   183
        public static String getRequestCharset(HttpExchange ex) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   184
            String charset = null;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   185
            List<String> contentType = ex.getRequestHeaders().get("Content-type");
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   186
            if (contentType != null) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   187
                for (String kv : contentType) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   188
                    for (String value : kv.split(";")) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   189
                        value = value.trim();
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   190
                        if (value.toLowerCase().startsWith("charset=")) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   191
                            charset = value.substring("charset=".length());
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   192
                        }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   193
                    }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   194
                }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   195
            }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   196
            return charset;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   197
        }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   198
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   199
        public static String getAcceptCharset(HttpExchange ex) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   200
            List<String> acceptCharset = ex.getRequestHeaders().get("Accept-Charset");
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   201
            if (acceptCharset != null && acceptCharset.size() > 0) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   202
                return acceptCharset.get(0);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   203
            }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   204
            return null;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   205
        }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   206
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   207
        public static String getGetRequestResource(HttpExchange ex, String charset) throws UnsupportedEncodingException {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   208
            String httpHandlerPath = ex.getHttpContext().getPath();
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   209
            String requestURIpath = ex.getRequestURI().getPath();
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   210
            String getRequestPath = requestURIpath.substring(httpHandlerPath.length());
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   211
            if (charset != null) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   212
                return URLDecoder.decode(getRequestPath, charset);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   213
            } else {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   214
                return getRequestPath;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   215
            }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   216
        }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   217
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   218
        public static String getGetRequestQuery(HttpExchange ex, String charset) throws UnsupportedEncodingException {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   219
            String query = ex.getRequestURI().getQuery();
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   220
            if (charset != null && query != null) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   221
                return URLDecoder.decode(query, charset);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   222
            } else {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   223
                return query;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   224
            }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   225
        }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   226
    }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   227
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   228
    private class BasicAuthenticator extends Authenticator {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   229
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   230
        @Override
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   231
        public Authenticator.Result authenticate(HttpExchange he) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   232
            Headers rmap = he.getRequestHeaders();
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   233
            String auth = rmap.getFirst("Authorization");
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   234
            if (auth == null) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   235
                Headers map = he.getResponseHeaders();
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   236
                map.set("WWW-Authenticate", "Basic realm=" + realm);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   237
                return new Authenticator.Retry(401);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   238
            }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   239
            int sp = auth.indexOf(' ');
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   240
            if (sp == -1 || !auth.substring(0, sp).equals("Basic")) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   241
                return new Authenticator.Failure(401);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   242
            }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   243
            byte[] b = Base64.getDecoder().decode(auth.substring(sp + 1));
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   244
            String credentials = new String(b);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   245
            int colon = credentials.indexOf(':');
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   246
            String uname = credentials.substring(0, colon);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   247
            String pass = credentials.substring(colon + 1);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   248
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   249
            if (authMBeanServer.containsKey(credentials)) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   250
                return new Authenticator.Success(new HttpPrincipal(uname, realm));
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   251
            } else {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   252
                Subject subject = null;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   253
                if (authenticator != null) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   254
                    String[] credential = new String[]{uname, pass};
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   255
                    try {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   256
                        subject = authenticator.authenticate(credential);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   257
                    } catch (SecurityException e) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   258
                        return new Authenticator.Failure(400);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   259
                    }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   260
                }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   261
                MBeanServer proxy = getMBeanServerProxy(mbeanServer, subject);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   262
                authMBeanServer.put(credentials, proxy);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   263
                return new Authenticator.Success(new HttpPrincipal(uname, realm));
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   264
            }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   265
        }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   266
    }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   267
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   268
    private class AuthInvocationHandler implements InvocationHandler {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   269
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   270
        private final MBeanServer mbeanServer;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   271
        private final AccessControlContext acc;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   272
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   273
        public AuthInvocationHandler(MBeanServer server, Subject subject) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   274
            this.mbeanServer = server;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   275
            if (subject == null) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   276
                this.acc = null;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   277
            } else {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   278
                if (SubjectDelegator.checkRemoveCallerContext(subject)) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   279
                    acc = JMXSubjectDomainCombiner.getDomainCombinerContext(subject);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   280
                } else {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   281
                    acc = JMXSubjectDomainCombiner.getContext(subject);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   282
                }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   283
            }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   284
        }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   285
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   286
        @Override
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   287
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   288
            if (acc == null) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   289
                return method.invoke(mbeanServer, args);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   290
            } else {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   291
                PrivilegedAction<Object> op = () -> {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   292
                    try {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   293
                        return method.invoke(mbeanServer, args);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   294
                    } catch (Exception ex) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   295
                    }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   296
                    return null;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   297
                };
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   298
                return AccessController.doPrivileged(op, acc);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   299
            }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   300
        }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   301
    }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   302
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   303
    private class DefaultHTTPHandler implements HttpHandler {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   304
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   305
        Map<String, List<String>> allowedMbeans = new HashMap<>();
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   306
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   307
        DefaultHTTPHandler() {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   308
        }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   309
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   310
        @Override
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   311
        public void handle(HttpExchange he) throws IOException {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   312
            MBeanServer server = mbeanServer;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   313
            if (env.get("jmx.remote.x.authentication") != null) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   314
                Headers rmap = he.getRequestHeaders();
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   315
                String auth = rmap.getFirst("Authorization");
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   316
                int sp = auth.indexOf(' ');
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   317
                byte[] b = Base64.getDecoder().decode(auth.substring(sp + 1));
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   318
                String authCredentials = new String(b);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   319
                server = authMBeanServer.get(authCredentials);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   320
                if (server == null) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   321
                    throw new IllegalArgumentException("Invalid HTTP request Headers");
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   322
                }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   323
            }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   324
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   325
            String charset = HttpUtil.getRequestCharset(he);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   326
            try {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   327
                switch (he.getRequestMethod()) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   328
                    case "GET":
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   329
                        JSONObject resp = getHandler.handle(HttpUtil.getGetRequestResource(he, charset),
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   330
                                HttpUtil.getGetRequestQuery(he, charset));
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   331
                        sendResponse(he, resp.toJsonString(), ((Long) (((JSONPrimitive) resp.get("status")).getValue())).intValue(), charset);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   332
                        break;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   333
                    case "POST":
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   334
                        String requestBody = readRequestBody(he, charset);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   335
                        List<JSONObject> responses = postHandler.handle(requestBody);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   336
                        if (responses.size() == 1) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   337
                            JSONObject jobj = responses.get(0);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   338
                            sendResponse(he, jobj.toJsonString(), ((Long) (((JSONPrimitive) responses.get(0).get("status")).getValue())).intValue(), charset);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   339
                        } else {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   340
                            int finalCode = HttpURLConnection.HTTP_OK;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   341
                            boolean isHttpOkPresent = responses.stream()
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   342
                                    .filter(r -> ((Long) (((JSONPrimitive) responses.get(0).get("status")).getValue())).intValue() == HttpURLConnection.HTTP_OK)
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   343
                                    .findFirst().isPresent();
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   344
                            if (!isHttpOkPresent) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   345
                                finalCode = ((Long) (((JSONPrimitive) responses.get(0).get("status")).getValue())).intValue();
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   346
                            }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   347
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   348
                            JSONArray jarr = new JSONArray();
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   349
                            responses.forEach(r -> jarr.add(r));
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   350
                            JSONObject jobj = new JSONObject();
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   351
                            jobj.put("status", new JSONPrimitive(finalCode));
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   352
                            jobj.put("result", jarr);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   353
                            String finalResult = jobj.toJsonString();
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   354
                            sendResponse(he, finalResult, finalCode, charset);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   355
                        }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   356
                        break;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   357
                    default:
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   358
                        sendResponse(he, "Not supported", HttpURLConnection.HTTP_BAD_METHOD, charset);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   359
                        break;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   360
                }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   361
            } catch (Throwable t) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   362
                t.printStackTrace();
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   363
            }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   364
        }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   365
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   366
        private String readRequestBody(HttpExchange he, String charset) throws IOException {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   367
            StringBuilder stringBuilder = new StringBuilder();
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   368
            InputStreamReader in = charset != null ? new InputStreamReader(he.getRequestBody(), charset) : new InputStreamReader(he.getRequestBody());
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   369
            BufferedReader br = new BufferedReader(in);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   370
            String line;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   371
            while ((line = br.readLine()) != null) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   372
                String decode = charset != null ? URLDecoder.decode(line, charset) : line;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   373
                stringBuilder.append(decode);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   374
            }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   375
            return stringBuilder.toString();
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   376
        }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   377
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   378
        private void sendResponse(HttpExchange exchange, String response, int code, String charset) throws IOException {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   379
            String acceptCharset = HttpUtil.getAcceptCharset(exchange);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   380
            if (acceptCharset != null) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   381
                charset = acceptCharset;
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   382
            }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   383
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   384
            // Set response headers explicitly
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   385
            String msg = charset == null ? response : URLEncoder.encode(response, charset);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   386
            byte[] bytes = msg.getBytes();
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   387
            Headers resHeaders = exchange.getResponseHeaders();
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   388
            resHeaders.add("Content-Type", "application/json; charset=" + charset);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   389
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   390
            exchange.sendResponseHeaders(code, bytes.length);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   391
            try (OutputStream os = exchange.getResponseBody()) {
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   392
                os.write(bytes);
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   393
            }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   394
        }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   395
    }
0c5a02edfdef REST Adapter Initial commit
hb
parents:
diff changeset
   396
}