com.linecorp.armeria.server.docs.DocService.java Source code

Java tutorial

Introduction

Here is the source code for com.linecorp.armeria.server.docs.DocService.java

Source

/*
 * Copyright 2015 LINE Corporation
 *
 * LINE Corporation licenses this file to you 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 com.linecorp.armeria.server.docs;

import static com.linecorp.armeria.server.composition.CompositeServiceEntry.ofCatchAll;
import static com.linecorp.armeria.server.composition.CompositeServiceEntry.ofExact;
import static java.util.Objects.requireNonNull;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import org.apache.thrift.TBase;

import com.fasterxml.jackson.databind.ObjectMapper;

import com.linecorp.armeria.server.Server;
import com.linecorp.armeria.server.ServerConfig;
import com.linecorp.armeria.server.ServerListenerAdapter;
import com.linecorp.armeria.server.ServiceConfig;
import com.linecorp.armeria.server.VirtualHost;
import com.linecorp.armeria.server.composition.AbstractCompositeService;
import com.linecorp.armeria.server.http.HttpService;
import com.linecorp.armeria.server.http.file.HttpFileService;
import com.linecorp.armeria.server.http.file.HttpVfs;
import com.linecorp.armeria.server.thrift.ThriftService;

/**
 * An {@link HttpService} that provides information about the {@link ThriftService}s running in a
 * {@link Server}. It does not require any configuration besides adding it to a {@link VirtualHost}; it
 * discovers all {@link ThriftService}s in the {@link Server} automatically.
 */
public class DocService extends AbstractCompositeService {

    private static final ObjectMapper mapper = new ObjectMapper();

    private final Map<Class<?>, ? extends TBase<?, ?>> sampleRequests;

    private Server server;

    /**
     * Creates a new instance, prepopulating debug forms with the provided {@code sampleRequests}.
     * {@code sampleRequests} should be a list of Thrift argument objects for methods that should be
     * prepopulated (e.g., a populated hello_args object for the hello method on HelloService).
     */
    @SafeVarargs
    public <T extends TBase<?, ?>> DocService(T... sampleRequests) {
        this(Arrays.asList(requireNonNull(sampleRequests, "sampleRequests")));
    }

    /**
     * Creates a new instance, prepopulating debug forms with the provided {@code sampleRequests}.
     * {@code sampleRequests} should be a list of Thrift argument objects for methods that should be
     * prepopulated (e.g., a populated hello_args object for the hello method on HelloService).
     */
    public DocService(Iterable<? extends TBase<?, ?>> sampleRequests) {
        super(ofExact("/specification.json", HttpFileService.forVfs(new DocServiceVfs())),
                ofCatchAll(HttpFileService.forClassPath(DocService.class.getClassLoader(),
                        "com/linecorp/armeria/server/docs")));
        requireNonNull(sampleRequests, "sampleRequests");
        this.sampleRequests = StreamSupport.stream(sampleRequests.spliterator(), false)
                .collect(Collectors.toMap(Object::getClass, Function.identity()));
    }

    @Override
    public void serviceAdded(ServiceConfig cfg) throws Exception {
        super.serviceAdded(cfg);

        if (server != null) {
            if (server != cfg.server()) {
                throw new IllegalStateException("cannot be added to more than one server");
            } else {
                return;
            }
        }

        server = cfg.server();

        // Build the Specification after all the services are added to the server.
        server.addListener(new ServerListenerAdapter() {
            @Override
            public void serverStarting(Server server) throws Exception {
                final ServerConfig config = server.config();
                final List<VirtualHost> virtualHosts = config.findVirtualHosts(DocService.this);

                final List<ServiceConfig> services = config.serviceConfigs().stream()
                        .filter(se -> virtualHosts.contains(se.virtualHost())).collect(Collectors.toList());

                vfs().setSpecification(mapper.writerWithDefaultPrettyPrinter()
                        .writeValueAsBytes(Specification.forServiceConfigs(services, sampleRequests)));
            }
        });
    }

    DocServiceVfs vfs() {
        return (DocServiceVfs) ((HttpFileService) serviceAt(0)).config().vfs();
    }

    static final class DocServiceVfs implements HttpVfs {

        private volatile Entry entry = Entry.NONE;

        @Override
        public Entry get(String path) {
            return entry;
        }

        void setSpecification(byte[] content) {
            assert entry == Entry.NONE;
            entry = new ByteArrayEntry("/", "application/json", content);
        }
    }
}