alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Glassfish example source code file (AdminAdapter.java)

This example Glassfish source code file (AdminAdapter.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Glassfish tags/keywords

actionreport, actionreport, adminadapter, exception, exception, grizzlyrequest, grizzlyresponse, inject, inject, ioexception, log, net, network, parametermap, string, string, stringtokenizer, threading, threads, util

The Glassfish AdminAdapter.java source code

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package com.sun.enterprise.v3.admin;

import com.sun.enterprise.config.serverbeans.Config;
import com.sun.enterprise.config.serverbeans.AdminService;
import com.sun.enterprise.config.serverbeans.Domain;
import com.sun.enterprise.config.serverbeans.SecureAdmin;
import com.sun.enterprise.config.serverbeans.Server;
import com.sun.enterprise.module.ModulesRegistry;
import com.sun.enterprise.module.common_impl.LogHelper;
import com.sun.enterprise.util.LocalStringManagerImpl;
import com.sun.enterprise.util.net.NetUtils;
import com.sun.grizzly.tcp.Request;
import com.sun.logging.LogDomains;
import java.security.Principal;
import org.glassfish.admin.payload.PayloadImpl;
import org.glassfish.api.ActionReport;
import org.glassfish.api.admin.*;
import org.glassfish.api.event.Events;
import org.glassfish.api.event.EventListener;
import org.glassfish.api.container.Adapter;
import org.jvnet.hk2.annotations.Inject;
import org.jvnet.hk2.component.PostConstruct;

import java.net.InetAddress;
import java.net.URLDecoder;
import java.util.StringTokenizer;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sun.enterprise.util.SystemPropertyConstants;
import org.glassfish.server.ServerEnvironmentImpl;

import java.net.HttpURLConnection;
import com.sun.enterprise.universal.GFBase64Decoder;
import com.sun.enterprise.v3.admin.adapter.AdminEndpointDecider;
import com.sun.enterprise.v3.admin.listener.GenericJavaConfigListener;
import com.sun.enterprise.v3.admin.listener.SystemPropertyListener;
import com.sun.grizzly.tcp.http11.GrizzlyAdapter;
import com.sun.grizzly.tcp.http11.GrizzlyRequest;
import com.sun.grizzly.tcp.http11.GrizzlyResponse;

import java.io.*;
import java.util.HashMap;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;

import org.glassfish.api.event.EventTypes;
import org.glassfish.api.event.RestrictTo;
import org.glassfish.internal.api.*;
import org.jvnet.hk2.component.Habitat;
import org.jvnet.hk2.config.ObservableBean;
import org.jvnet.hk2.config.ConfigSupport;

/**
 * Listen to admin commands...
 * @author dochez
 */
public abstract class AdminAdapter extends GrizzlyAdapter implements Adapter, PostConstruct, EventListener {

    public final static String VS_NAME="__asadmin";
    public final static String PREFIX_URI = "/" + VS_NAME;
    public final static Logger logger = LogDomains.getLogger(ServerEnvironmentImpl.class, LogDomains.ADMIN_LOGGER);
    public final static LocalStringManagerImpl adminStrings = new LocalStringManagerImpl(AdminAdapter.class);
    private final static String GET = "GET";
    private final static String POST = "POST";
    private static final GFBase64Decoder decoder = new GFBase64Decoder();
    private static final String BASIC = "Basic ";

    private static final String QUERY_STRING_SEPARATOR = "&";
    private static final String ASADMIN_CMD_PREFIX = "AS_ADMIN_";

    private static final String[] authRelatedHeaderNames = {
        SecureAdmin.Util.ADMIN_INDICATOR_HEADER_NAME,
        SecureAdmin.Util.ADMIN_ONE_TIME_AUTH_TOKEN_HEADER_NAME};

    private static final String DAS_LOOK_FOR_CERT_PROPERTY_NAME = "org.glassfish.admin.DASCheckAdminCert";

    @Inject
    ModulesRegistry modulesRegistry;

    @Inject
    CommandRunnerImpl commandRunner;

    @Inject
    ServerEnvironmentImpl env;


    @Inject
    Events events;
    
    @Inject(name=ServerEnvironment.DEFAULT_INSTANCE_NAME)
    Config config;

    private AdminEndpointDecider epd = null;
    
    @Inject
    ServerContext sc;

    @Inject
    Habitat habitat;

    @Inject
    volatile AdminService as = null;

    @Inject
    volatile Domain domain;

    @Inject(name=ServerEnvironment.DEFAULT_INSTANCE_NAME)
    private volatile Server server;

    @Inject
    GenericJavaConfigListener listener;
    
    private SecureAdmin secureAdmin;

    final Class<? extends Privacy> privacyClass;

    private boolean isRegistered = false;
            
    CountDownLatch latch = new CountDownLatch(1);

    protected AdminAdapter(Class<? extends Privacy> privacyClass) {
        this.privacyClass = privacyClass;
    }

    public void postConstruct() {
        events.register(this);
        
        epd = new AdminEndpointDecider(config, logger);
        registerDynamicReconfigListeners();
        this.setHandleStaticResources(true);
        this.addRootFolder(env.getProps().get(SystemPropertyConstants.INSTANCE_ROOT_PROPERTY) + "/asadmindocroot/");
        secureAdmin = habitat.getComponent(SecureAdmin.class);
    }

    /**
     * Call the service method, and notify all listeners
     *
     * @exception Exception if an error happens during handling of
     *   the request. Common errors are:
     *   <ul>
  • IOException if an input/output error occurs and we are * processing an included servlet (otherwise it is swallowed and * handled by the top level error handler mechanism) * <li>ServletException if a servlet throws an exception and * we are processing an included servlet (otherwise it is swallowed * and handled by the top level error handler mechanism) * </ul> * Tomcat should be able to handle and log any other exception ( including * runtime exceptions ) */ public void service(GrizzlyRequest req, GrizzlyResponse res) { LogHelper.getDefaultLogger().finer("Admin adapter !"); LogHelper.getDefaultLogger().finer("Received something on " + req.getRequestURI()); LogHelper.getDefaultLogger().finer("QueryString = " + req.getQueryString()); String requestURI = req.getRequestURI(); /* if (requestURI.startsWith("/__asadmin/ADMINGUI")) { super.service(req, res); }*/ ActionReport report = getClientActionReport(requestURI, req); // remove the qualifier if necessary if (requestURI.indexOf('.')!=-1) { requestURI = requestURI.substring(0, requestURI.indexOf('.')); } Payload.Outbound outboundPayload = PayloadImpl.Outbound.newInstance(); try { if (!latch.await(20L, TimeUnit.SECONDS)) { report = this.getClientActionReport(req.getRequestURI(), req); report.setActionExitCode(ActionReport.ExitCode.FAILURE); report.setMessage("V3 cannot process this command at this time, please wait"); } else { if (!authenticate(req, report, res)) return; report = doCommand(requestURI, req, report, outboundPayload); } } catch(InterruptedException e) { report.setActionExitCode(ActionReport.ExitCode.FAILURE); report.setMessage("V3 cannot process this command at this time, please wait"); } catch (Exception e) { report.setActionExitCode(ActionReport.ExitCode.FAILURE); report.setMessage("Exception while processing command: " + e); } try { res.setStatus(200); /* * Format the command result report into the first part (part #0) of * the outbound payload and set the response's content type based * on the payload's. If the report is the only part then the * stream will be written as content type text/something and * will contain only the report. If the payload already has * content - such as files to be downloaded, for example - then the * content type of the payload reflects its multi-part nature and * an implementation-specific content type will be set in the response. */ ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); report.writeReport(baos); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); final Properties reportProps = new Properties(); reportProps.setProperty("data-request-type", "report"); outboundPayload.addPart(0, report.getContentType(), "report", reportProps, bais); res.setContentType(outboundPayload.getContentType()); outboundPayload.writeTo(res.getOutputStream()); res.getOutputStream().flush(); res.finishResponse(); } catch (Exception e) { throw new RuntimeException(e); } } public AdminAccessController.Access authenticate(GrizzlyRequest req) throws Exception { final Request r = req.getRequest(); String[] up = getUserPassword(r); String user = up[0]; String password = up.length > 1 ? up[1] : ""; AdminAccessController authenticator = habitat.getByContract(AdminAccessController.class); if (authenticator != null) { /* * If an admin request includes a large payload and secure admin is * enabled and the request does NOT include a client cert, then * the getUsePrincipal invocation can cause problems. So normally * the DAS will not look for a client cert. To override this, the user can * set org.glassfish.admin.DASCheckAdminCert=true but s/he should realize * that this can cause problems with large uploads if secure admin * is enabled and no client cert is present. */ final Principal sslPrincipal = ! env.isDas() || Boolean.getBoolean(DAS_LOOK_FOR_CERT_PROPERTY_NAME) ? req.getUserPrincipal() : null; return authenticator.loginAsAdmin(user, password, as.getAuthRealmName(), req.getRemoteHost(), authRelatedHeaders(req), sslPrincipal); } return AdminAccessController.Access.FULL; //if the authenticator is not available, allow all access - per Jerome } private Map<String,String> authRelatedHeaders(final GrizzlyRequest gr) { final Map<String,String> result = new HashMap(); for (String authRelatedHeaderName : authRelatedHeaderNames) { final String value = gr.getHeader(authRelatedHeaderName); if (value != null) { result.put(authRelatedHeaderName, value); } } return result; } /** A convenience method to extract user name from a request. It assumes the HTTP Basic Auth. * * @param req instance of Request * @return a two-element string array. If Auth header exists and can be correctly decoded, returns the user name * and password as the two elements. If any error occurs or if the header does not exist, returns an array with * two blank strings. Never returns a null. * @throws IOException in case of error with decoding the buffer (HTTP basic auth) */ public static String[] getUserPassword(Request req) throws IOException { //implementation note: other adapters make use of this method String authHeader = req.getHeader("Authorization"); if (authHeader == null) { return new String[]{"", ""}; } String enc = authHeader.substring(BASIC.length()); String dec = new String(decoder.decodeBuffer(enc)); int i = dec.indexOf(':'); if (i < 0) return new String[] { "", "" }; return new String[] { dec.substring(0, i), dec.substring(i + 1) }; } private boolean authenticate(GrizzlyRequest req, ActionReport report, GrizzlyResponse res) throws Exception { AdminAccessController.Access access = authenticate(req); /* * Admin requests through this adapter are assumed to change the * configuration, which means the access granted needs to be FULL. */ switch (access) { case FULL: return true; case MONITORING: /* * The request authenticated OK but it is remote and this is the DAS; * that's why MONITORING rather than FULL came back. * * For user-friendliness respond with Forbidden. */ reportAuthFailure(res, report, "adapter.auth.remoteReqSecAdminOff", "Remote configuration is currently disabled", HttpURLConnection.HTTP_FORBIDDEN); break; case NONE: if (env.isDas()) { reportAuthFailure(res, report, "adapter.auth.userpassword", "Invalid user name or password", HttpURLConnection.HTTP_UNAUTHORIZED, "WWW-Authenticate", "BASIC"); } else { reportAuthFailure(res, report, "adapter.auth.notOnInstance", "Configuration access to an instance is not allowed; please connect to the domain admin server instead to make configuration changes", HttpURLConnection.HTTP_FORBIDDEN); } break; } return access == AdminAccessController.Access.FULL; } private void reportAuthFailure(final GrizzlyResponse res, final ActionReport report, final String msgKey, final String msg, final int httpStatus) throws IOException { reportAuthFailure(res, report, msgKey, msg, httpStatus, null, null); } private void reportAuthFailure(final GrizzlyResponse res, final ActionReport report, final String msgKey, final String msg, final int httpStatus, final String headerName, final String headerValue) throws IOException { report.setActionExitCode(ActionReport.ExitCode.FAILURE); report.setMessage(adminStrings.getLocalString(msgKey, msg)); report.setActionDescription("Authentication error"); res.setStatus(httpStatus); if (headerName != null) { res.setHeader(headerName, headerValue); } res.setContentType(report.getContentType()); report.writeReport(res.getOutputStream()); res.getOutputStream().flush(); res.finishResponse(); } private ActionReport getClientActionReport(String requestURI, GrizzlyRequest req) { ActionReport report=null; // first we look at the command extension (ie list-applications.[json | html | mf] if (requestURI.indexOf('.')!=-1) { String qualifier = requestURI.substring(requestURI.indexOf('.')+1); report = habitat.getComponent(ActionReport.class, qualifier); } else { String userAgent = req.getHeader("User-Agent"); if (userAgent!=null) report = habitat.getComponent(ActionReport.class, userAgent.substring(userAgent.indexOf('/')+1)); if (report==null) { String accept = req.getHeader("Accept"); if (accept!=null) { StringTokenizer st = new StringTokenizer(accept, ","); while (report==null && st.hasMoreElements()) { final String scheme=st.nextToken(); report = habitat.getComponent(ActionReport.class, scheme.substring(scheme.indexOf('/')+1)); } } } } if (report==null) { // get the default one. report = habitat.getComponent(ActionReport.class, "html"); } return report; } protected abstract boolean validatePrivacy(AdminCommand command); private ActionReport doCommand(String requestURI, GrizzlyRequest req, ActionReport report, Payload.Outbound outboundPayload) { if (!requestURI.startsWith(getContextRoot())) { String msg = adminStrings.getLocalString("adapter.panic", "Wrong request landed in AdminAdapter {0}", requestURI); report.setMessage(msg); LogHelper.getDefaultLogger().info(msg); return report; } // wbn handle no command and no slash-suffix String command = ""; if (requestURI.length() > getContextRoot().length() + 1) command = requestURI.substring(getContextRoot().length() + 1); final ParameterMap parameters = extractParameters(req.getQueryString()); try { Payload.Inbound inboundPayload = PayloadImpl.Inbound.newInstance( req.getContentType(), req.getInputStream()); if (logger.isLoggable(Level.FINE)) { logger.fine("***** AdminAdapter "+req.getMethod()+" *****"); } AdminCommand adminCommand = commandRunner.getCommand(command, report, logger); if (adminCommand==null) { // maybe commandRunner already reported the failure? if (report.getActionExitCode() == ActionReport.ExitCode.FAILURE) return report; String message = adminStrings.getLocalString("adapter.command.notfound", "Command {0} not found", command); // cound't find command, not a big deal logger.log(Level.FINE, message); report.setMessage(message); report.setActionExitCode(ActionReport.ExitCode.FAILURE); return report; } if (validatePrivacy(adminCommand)) { //if (adminCommand.getClass().getAnnotation(Visibility.class).privacy().equals(visibility.privacy())) { // todo : needs to be changed, we should reuse adminCommand CommandRunner.CommandInvocation inv = commandRunner.getCommandInvocation(command, report); inv.parameters(parameters).inbound(inboundPayload).outbound(outboundPayload).execute(); try { // note it has become extraordinarily difficult to change the reporter! CommandRunnerImpl.ExecutionContext inv2 = (CommandRunnerImpl.ExecutionContext) inv; report = inv2.report(); } catch(Exception e) { } } else { report.failure( logger, adminStrings.getLocalString("adapter.wrongprivacy", "Command {0} does not have {1} visibility", command, privacyClass.getSimpleName().toLowerCase()), null); return report; } } catch (Throwable t) { /* * Must put the error information into the report * for the client to see it. */ report.setActionExitCode(ActionReport.ExitCode.FAILURE); report.setFailureCause(t); report.setMessage(t.getLocalizedMessage()); report.setActionDescription("Last-chance AdminAdapter exception handler"); } return report; } /** * Finish the response and recycle the request/response tokens. Base on * the connection header, the underlying socket transport will be closed */ public void afterService(GrizzlyRequest req, GrizzlyResponse res) throws Exception { } /** * Notify all container event listeners that a particular event has * occurred for this Adapter. The default implementation performs * this notification synchronously using the calling thread. * * @param type Event type * @param data Event data */ public void fireAdapterEvent(String type, Object data) { } /** * extract parameters from URI and save it in ParameterMap obj * * @params requestString string URI to extract * * @returns ParameterMap */ ParameterMap extractParameters(final String requestString) { // extract parameters... final ParameterMap parameters = new ParameterMap(); StringTokenizer stoken = new StringTokenizer(requestString == null ? "" : requestString, QUERY_STRING_SEPARATOR); while (stoken.hasMoreTokens()) { String token = stoken.nextToken(); if (token.indexOf("=") == -1) continue; String paramName = null; String value = null; paramName = token.substring(0, token.indexOf("=")); value = token.substring(token.indexOf("=") + 1); try { value = URLDecoder.decode(value, "UTF-8"); } catch (UnsupportedEncodingException e) { logger.log(Level.WARNING, adminStrings.getLocalString("adapter.param.decode", "Cannot decode parameter {0} = {1}")); } // indicates a password parameter if (paramName.startsWith(ASADMIN_CMD_PREFIX) && (value != null)) { try { value = new String(decoder.decodeBuffer(value)); } catch (IOException e) { // ignore for now. Not much can be done anyway. // todo: improve this error condition reporting } } parameters.add(paramName, value); } // Dump parameters... if (logger.isLoggable(Level.FINER)) { for (Map.Entry<String, List entry : parameters.entrySet()) { for (String v : entry.getValue()) logger.finer("Key " + entry.getKey() + " = " + v); } } return parameters; } public void event(@RestrictTo(EventTypes.SERVER_READY_NAME) Event event) { if (event.is(EventTypes.SERVER_READY)) { latch.countDown(); logger.fine("Ready to receive administrative commands"); } //the count-down does not start if any other event is received } public int getListenPort() { return epd.getListenPort(); } @Override public InetAddress getListenAddress() { return epd.getListenAddress(); } public List<String> getVirtualServers() { return epd.getAsadminHosts(); } /** * Checks whether this adapter has been registered as a network endpoint. */ public boolean isRegistered() { return isRegistered; } /** * Marks this adapter as having been registered or unregistered as a * network endpoint */ public void setRegistered(boolean isRegistered) { this.isRegistered = isRegistered; } private void registerSystemPropertyListener() { ObservableBean ob = (ObservableBean)ConfigSupport.getImpl(domain); SystemPropertyListener ls = habitat.getComponent(SystemPropertyListener.class); ob.addListener(ls); //there should be a better way to do this ... ob = (ObservableBean)ConfigSupport.getImpl(server); ob.addListener(ls); ob = (ObservableBean)ConfigSupport.getImpl(config); ob.addListener(ls); } private void registerDynamicReconfigListeners() { registerSystemPropertyListener(); } }
  • Other Glassfish examples (source code examples)

    Here is a short list of links related to this Glassfish AdminAdapter.java source code file:

    ... this post is sponsored by my books ...

    #1 New Release!

    FP Best Seller

     

    new blog posts

     

    Copyright 1998-2021 Alvin Alexander, alvinalexander.com
    All Rights Reserved.

    A percentage of advertising revenue from
    pages under the /java/jwarehouse URI on this website is
    paid back to open source projects.