001/*
002 * (C) Copyright 2016 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl-2.1.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Stephane Lacoin at Nuxeo (aka matic)
016 */
017package org.nuxeo.connect.tools.report;
018
019import java.io.IOException;
020import java.io.OutputStream;
021import java.net.Socket;
022import java.time.Instant;
023import java.util.Arrays;
024import java.util.HashSet;
025import java.util.Iterator;
026import java.util.Set;
027
028import org.nuxeo.connect.tools.report.ReportConfiguration.Contribution;
029import org.nuxeo.ecm.core.management.statuses.NuxeoInstanceIdentifierHelper;
030import org.nuxeo.runtime.api.Framework;
031import org.nuxeo.runtime.management.ResourcePublisher;
032import org.nuxeo.runtime.model.ComponentContext;
033import org.nuxeo.runtime.model.ComponentInstance;
034import org.nuxeo.runtime.model.DefaultComponent;
035
036/**
037 * Reports aggregator, exposed as a service in the runtime.
038 *
039 * @since 8.3
040 */
041public class ReportComponent extends DefaultComponent {
042
043    public static ReportComponent instance;
044
045    public ReportComponent() {
046        instance = this;
047    }
048
049    final ReportConfiguration configuration = new ReportConfiguration();
050
051    final Service service = new Service();
052
053    class Service implements ReportRunner {
054        @Override
055        public Set<String> list() {
056            Set<String> names = new HashSet<>();
057            for (Contribution contrib : configuration) {
058                names.add(contrib.name);
059            }
060            return names;
061        }
062
063        @Override
064        public void run(OutputStream out, Set<String> names) throws IOException {
065            out.write('{');
066            out.write('"');
067            out.write(NuxeoInstanceIdentifierHelper.getServerInstanceName().getBytes());
068            out.write('"');
069            out.write(':');
070            out.write('{');
071            Iterator<Contribution> iterator = configuration.iterator(names);
072            while (iterator.hasNext()) {
073                Contribution contrib = iterator.next();
074                out.write('"');
075                out.write(contrib.name.getBytes());
076                out.write('"');
077                out.write(':');
078                contrib.writer.write(out);
079                if (iterator.hasNext()) {
080                    out.write(',');
081                }
082                out.flush();
083            }
084            out.write('}');
085            out.write('}');
086            out.flush();
087        }
088    }
089
090    final Management management = new Management();
091
092    public class Management implements ReportServer {
093
094        @Override
095        public void run(String host, int port, String... names) throws IOException {
096            ClassLoader tcl = Thread.currentThread().getContextClassLoader();
097            Thread.currentThread().setContextClassLoader(Management.class.getClassLoader());
098            try (Socket sock = new Socket(host, port); OutputStream sink = sock.getOutputStream()) {
099                service.run(sink, new HashSet<>(Arrays.asList(names)));
100            } finally {
101                Thread.currentThread().setContextClassLoader(tcl);
102            }
103        }
104
105    }
106
107    @Override
108    public void start(ComponentContext context) {
109        Framework.getService(ResourcePublisher.class).registerResource("connect-report", "connect-report", ReportServer.class, management);
110    }
111
112    @Override
113    public void stop(ComponentContext context) {
114        Framework.getService(ResourcePublisher.class).unregisterResource("connect-report", "connect-report");
115    }
116
117
118    @Override
119    public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
120        if (contribution instanceof Contribution) {
121            configuration.addContribution((Contribution) contribution);
122        } else {
123            throw new IllegalArgumentException(
124                    String.format("unknown contribution of type %s in %s", contribution.getClass(), contributor));
125        }
126    }
127
128    @Override
129    public <T> T getAdapter(Class<T> adapter) {
130        if (adapter.isAssignableFrom(Service.class)) {
131            return adapter.cast(service);
132        }
133        return super.getAdapter(adapter);
134    }
135
136}