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.util.Arrays;
023import java.util.HashSet;
024import java.util.Iterator;
025import java.util.Set;
026
027import org.nuxeo.connect.tools.report.ReportConfiguration.Contribution;
028import org.nuxeo.ecm.core.management.statuses.NuxeoInstanceIdentifierHelper;
029import org.nuxeo.runtime.api.Framework;
030import org.nuxeo.runtime.management.ResourcePublisher;
031import org.nuxeo.runtime.model.ComponentContext;
032import org.nuxeo.runtime.model.ComponentInstance;
033import org.nuxeo.runtime.model.DefaultComponent;
034
035/**
036 * Reports aggregator, exposed as a service in the runtime.
037 *
038 * @since 8.3
039 */
040public class ReportComponent extends DefaultComponent {
041
042    final ReportConfiguration configuration = new ReportConfiguration();
043
044    final Service service = new Service();
045
046    class Service implements ReportRunner {
047
048        public ReportConfiguration getConfiguration() {
049            return configuration;
050        }
051
052        @Override
053        public Set<String> list() {
054            Set<String> names = new HashSet<>();
055            for (Contribution contrib : configuration) {
056                names.add(contrib.name);
057            }
058            return names;
059        }
060
061        @Override
062        public void run(OutputStream out, Set<String> names) throws IOException {
063            out.write('{');
064            out.write('"');
065            out.write(NuxeoInstanceIdentifierHelper.getServerInstanceName().getBytes());
066            out.write('"');
067            out.write(':');
068            out.write('{');
069            Iterator<Contribution> iterator = configuration.iterator(names);
070            while (iterator.hasNext()) {
071                Contribution contrib = iterator.next();
072                out.write('"');
073                out.write(contrib.name.getBytes());
074                out.write('"');
075                out.write(':');
076                contrib.writer.write(out);
077                if (iterator.hasNext()) {
078                    out.write(',');
079                }
080                out.flush();
081            }
082            out.write('}');
083            out.write('}');
084            out.flush();
085        }
086    }
087
088    final Management management = new Management();
089
090    public class Management implements ReportServer {
091
092        @Override
093        public void run(String host, int port, String... names) throws IOException {
094            ClassLoader tcl = Thread.currentThread().getContextClassLoader();
095            Thread.currentThread().setContextClassLoader(Management.class.getClassLoader());
096            try (Socket sock = new Socket(host, port); OutputStream sink = sock.getOutputStream()) {
097                service.run(sink, new HashSet<>(Arrays.asList(names)));
098            } finally {
099                Thread.currentThread().setContextClassLoader(tcl);
100            }
101        }
102
103    }
104
105    @Override
106    public void start(ComponentContext context) {
107        Framework.getService(ResourcePublisher.class).registerResource("connect-report", "connect-report", ReportServer.class, management);
108    }
109
110    @Override
111    public void stop(ComponentContext context) {
112        Framework.getService(ResourcePublisher.class).unregisterResource("connect-report", "connect-report");
113    }
114
115
116    @Override
117    public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
118        if (contribution instanceof Contribution) {
119            configuration.addContribution((Contribution) contribution);
120        } else {
121            throw new IllegalArgumentException(
122                    String.format("unknown contribution of type %s in %s", contribution.getClass(), contributor));
123        }
124    }
125
126    @Override
127    public <T> T getAdapter(Class<T> adapter) {
128        if (adapter.isAssignableFrom(Service.class)) {
129            return adapter.cast(service);
130        }
131        return super.getAdapter(adapter);
132    }
133
134}