All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fish.payara.appserver.monitoring.rest.service.adapter.RestMonitoringAdapter Maven / Gradle / Ivy

There is a newer version: 6.2024.6
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) [2016-2021] Payara Foundation 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://github.com/payara/Payara/blob/master/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 glassfish/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * The Payara Foundation designates this particular file as subject to the "Classpath"
 * exception as provided by the Payara Foundation 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 fish.payara.appserver.monitoring.rest.service.adapter;

import com.sun.enterprise.config.serverbeans.Application;
import com.sun.enterprise.config.serverbeans.Config;
import com.sun.enterprise.config.serverbeans.Domain;
import com.sun.enterprise.config.serverbeans.SystemApplications;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import jakarta.annotation.PostConstruct;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import org.glassfish.api.admin.ServerEnvironment;
import org.glassfish.api.container.Adapter;
import org.glassfish.api.event.Events;
import org.glassfish.grizzly.http.Method;
import org.glassfish.grizzly.http.server.HttpHandler;
import org.glassfish.grizzly.http.server.Request;
import org.glassfish.grizzly.http.server.Response;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.internal.data.ApplicationRegistry;
import org.glassfish.server.ServerEnvironmentImpl;
import org.jvnet.hk2.annotations.Optional;
import org.jvnet.hk2.annotations.Service;
import fish.payara.appserver.monitoring.rest.service.configuration.RestMonitoringConfiguration;

/**
 * The adapter class for the Rest Monitoring application.
 * @author Andrew Pielage
 */
@Service
public final class RestMonitoringAdapter extends HttpHandler implements Adapter {
    private boolean isRegistered = false;
    private boolean appRegistered = false;
    private ResourceBundle bundle;
    private final Method[] allowedHttpMethods = {Method.GET, Method.POST, Method.HEAD, Method.DELETE, Method.PUT};

    private static RestMonitoringEndpointDecider endpointDecider;
    
    @Inject
    ServerEnvironmentImpl env;
    
    @Inject
    ApplicationRegistry appRegistry;
    
    @Inject
    Domain domain;
    
    @Inject
    ServiceLocator habitat;
    
    @Inject
    Events events;
    
    @Inject @Named(ServerEnvironment.DEFAULT_INSTANCE_NAME)
    Config serverConfig;
    
    @Inject
    @Named(ServerEnvironment.DEFAULT_INSTANCE_NAME)
    @Optional
    RestMonitoringConfiguration restMonitoringServiceConfiguration;
    
    private final static Logger logger = Logger.getLogger(RestMonitoringAdapter.class.getName());
    private final static String RESOURCE_PACKAGE = "fish/payara/appserver/monitoring/rest/service/adapter";
    private final CountDownLatch latch = new CountDownLatch(1);
    
    @PostConstruct
    public void postConstruct() {
        restMonitoringServiceConfiguration = habitat.getService(RestMonitoringConfiguration.class);
        init();
    }
    
    private void init() {
        try {
            endpointDecider = new RestMonitoringEndpointDecider(serverConfig, restMonitoringServiceConfiguration);
        } catch (Exception ex) {
            logger.log(Level.INFO, "Rest Monitoring Console cannot initialise", ex);
        }
        
        // If the app exists in the domain.xml AND it's registered to this instance
        if (appExistsInConfig() && (domain.getSystemApplicationReferencedFrom(env.getInstanceName(), 
                restMonitoringServiceConfiguration.getApplicationName()) != null)) {
            setAppRegistered(true);
        }
    }
    
    public boolean appExistsInConfig() {
        return (getSystemApplicationConfig() != null);
    }
    
    public boolean appExistsInConfig(String contextRoot) {
        return (getSystemApplicationConfig(contextRoot) != null);
    }
    
    /**
     * Gets the application config for the system application with the matching name or context root (in that order).
     * @return The application config, or null if there is no matching application
     */
    public Application getSystemApplicationConfig() {
        // First, check if there is an app registered for this server with the given application name
        Application application = domain.getSystemApplicationReferencedFrom(env.getInstanceName(), 
                restMonitoringServiceConfiguration.getApplicationName());
        
        // If the app hasn't been registered to the instance yet, the previous check will return null, so check for one 
        // with a matching context root instead (as these are also unique and saves us creating an extra app entry)
        if (application == null) {
            application = getApplicationWithMatchingContextRoot(getContextRoot());
        }
        
        return application;
    }
    
    /**
     * Gets the application config for the system application with the matching context root. This method is used over the 
     * overloaded method if you want to skip trying to get the application config based on the application name, such as
     * if you've reconfigured the application.
     * @param contextRoot The context root of the application
     * @return The application config, or null if there is no matching application.
     */
    public Application getSystemApplicationConfig(String contextRoot) {
        Application application = getApplicationWithMatchingContextRoot(contextRoot);
        
        return application;
    }
    
    /**
     * Helper method that searches through all system applications for one with a matching context root.
     * @param contextRoot The context root fo the application.
     * @return The application config, or null if there are no applications with a matching context root.
     */
    private Application getApplicationWithMatchingContextRoot(String contextRoot) {
        Application application = null;

        SystemApplications systemApplications = domain.getSystemApplications();
        for (Application systemApplication : systemApplications.getApplications()) {
            if (systemApplication.getContextRoot().equals(contextRoot)) {
                application = systemApplication;
                break;
            }
        }
        
        return application;
    }
    
    @Override
    public void service(Request request, Response response) throws Exception {
        bundle = getResourceBundle(request.getLocale());
        Method method = request.getMethod();
        
        if (!checkHttpMethodAllowed(method)) {
            response.setStatus(java.net.HttpURLConnection.HTTP_BAD_METHOD,
                    method.getMethodString() + " " + bundle.getString("http.bad.method"));
            response.setHeader("Allow", getAllowedHttpMethodsAsString());
            return;
        }

        try {
            if (!latch.await(100L, TimeUnit.SECONDS)) {
                logger.log(Level.SEVERE, "Timed out processing a rest monitoring request");
                return;
            }
        } catch (InterruptedException ex) {
            logger.log(Level.SEVERE, "Cannot process rest monitoring request");
            return;
        }
        
        if (isResourceRequest(request)) {
            try {
                handleResourceRequest(request, response);
            } catch (IOException ioe) {
                if (logger.isLoggable(Level.SEVERE)) {
                    logger.log(Level.SEVERE, "Unable to serve resource: {0}. Cause: {1}",
                            new Object[]{request.getRequestURI(), ioe.toString()});
                }
                
                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE, ioe.toString(), ioe);
                }
            }
            
            return;
        }
        
        response.setContentType("text/html; charset=UTF-8");
    }
    
    private ResourceBundle getResourceBundle(Locale locale) {
        return ResourceBundle.getBundle(RestMonitoringAdapter.class.getPackage().getName() + ".LocalStrings", locale);
    }
    
    private boolean checkHttpMethodAllowed(Method method) {
        for (Method allowedMethod : allowedHttpMethods) {
            if (allowedMethod.equals(method)) {
                return true;
            }
        }
        return false;
    }
    
    private String getAllowedHttpMethodsAsString() {
        StringBuilder sb = new StringBuilder(allowedHttpMethods[0].getMethodString());
        for (int i = 1; i < allowedHttpMethods.length; i++) {
            sb.append(", ").append(allowedHttpMethods[i].getMethodString());
        }
        
        return sb.toString();
    }
    
    private boolean isResourceRequest(Request request) {
        return (getContentType(request.getRequestURI()) != null);
    }
    
    private String getContentType(String resource) {
        if (resource == null || resource.length() == 0) {
            return null;
        }
        
        if (resource.endsWith(".gif")) {
            return "image/gif";
        } else if (resource.endsWith(".jpg")) {
            return "image/jpeg";
        } else {
            if (logger.isLoggable(Level.FINE)) {
                logger.log(Level.FINE, "Unhandled content-type: {0}", resource);
            }
            
            return null;
        }
    }
    
    private void handleResourceRequest(Request request, Response response) throws IOException {
        String resourcePath = RESOURCE_PACKAGE + request.getRequestURI();

        ClassLoader loader = RestMonitoringAdapter.class.getClassLoader();

        try (InputStream inputStream = loader.getResourceAsStream(resourcePath)) {
            if (inputStream == null) {
                logger.log(Level.WARNING, "Resource not found: {0}", resourcePath);
                return;
            }
            
            byte[] buffer = new byte[512];
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(512);
            
            for (int i = inputStream.read(buffer); i != -1; i = inputStream.read(buffer)) {
                byteArrayOutputStream.write(buffer, 0, i);
            }
            
            String contentType = getContentType(resourcePath);
            
            if (contentType != null) {
                response.setContentType(contentType);
            }
            
            response.setContentLength(byteArrayOutputStream.size());
            OutputStream outputStream = response.getOutputStream();
            byteArrayOutputStream.writeTo(outputStream);
            outputStream.flush();
        }
    }
    
    @Override
    public HttpHandler getHttpService() {
        return this;
    }

    @Override
    public String getContextRoot() {
        return endpointDecider == null ? null : endpointDecider.getContextRoot();
    }

    @Override
    public int getListenPort() {
        return endpointDecider == null ? -1 : endpointDecider.getListenPort();
    }

    @Override
    public InetAddress getListenAddress() {
        return endpointDecider == null ? null : endpointDecider.getListenAddress();
    }

    @Override
    public List getVirtualServers() {
        return endpointDecider == null ? Collections.emptyList() : endpointDecider.getHosts();
    }

    @Override
    public boolean isRegistered() {
        return isRegistered;
    }

    @Override
    public void setRegistered(boolean isRegistered) {
        this.isRegistered = isRegistered;
    }

    public boolean isAppRegistered() {
        return appRegistered;
    }
    
    public void setAppRegistered(boolean appRegistered) {
        this.appRegistered = true;
    }
    
    /**
     * Overloaded method that checks if an application with the provided context root has been registered to this instance.
     * @param contextRoot The context root to match.
     * @return True if an application has been registered to this instance.
     */
    public boolean isAppRegistered(String contextRoot) {
        boolean registered = false;
        Application application = getSystemApplicationConfig(contextRoot);
        
        // Check if we've found an application with a matching context root, and that it's registered to this instance
        if (application != null && (domain.getSystemApplicationReferencedFrom(env.getInstanceName(), 
                application.getName()) != null)) {
            registered = true;
        }
        
        return registered;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy