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.RuntimeServiceEvent; 030import org.nuxeo.runtime.RuntimeServiceListener; 031import org.nuxeo.runtime.api.Framework; 032import org.nuxeo.runtime.management.ResourcePublisher; 033import org.nuxeo.runtime.model.ComponentContext; 034import org.nuxeo.runtime.model.ComponentInstance; 035import org.nuxeo.runtime.model.DefaultComponent; 036import org.nuxeo.runtime.reload.ReloadService; 037import org.nuxeo.runtime.services.event.Event; 038import org.nuxeo.runtime.services.event.EventListener; 039import org.nuxeo.runtime.services.event.EventService; 040 041/** 042 * Reports aggregator, exposed as a service in the runtime. 043 * 044 * @since 8.3 045 */ 046public class ReportComponent extends DefaultComponent { 047 048 public static ReportComponent instance; 049 050 public ReportComponent() { 051 instance = this; 052 } 053 054 ReloadListener reloadListener; 055 056 class ReloadListener implements EventListener { 057 final ComponentContext context; 058 059 ReloadListener(ComponentContext context) { 060 this.context = context; 061 } 062 063 @Override 064 public void handleEvent(Event event) { 065 if (ReloadService.AFTER_RELOAD_EVENT_ID.equals(event.getId())) { 066 applicationStarted(context); 067 } else if (ReloadService.BEFORE_RELOAD_EVENT_ID.equals(event.getId())) { 068 applicationStopped(context); 069 } 070 } 071 072 } 073 074 @Override 075 public void activate(ComponentContext context) { 076 Framework.addListener(new RuntimeServiceListener() { 077 078 @Override 079 public void handleEvent(RuntimeServiceEvent event) { 080 if (RuntimeServiceEvent.RUNTIME_ABOUT_TO_STOP != event.id) { 081 return; 082 } 083 Framework.removeListener(this); 084 Framework.getService(EventService.class).removeListener(ReloadService.RELOAD_TOPIC, reloadListener); 085 } 086 }); 087 Framework.getService(EventService.class).addListener(ReloadService.RELOAD_TOPIC, 088 reloadListener = new ReloadListener(context)); 089 } 090 091 final ReportConfiguration configuration = new ReportConfiguration(); 092 093 final Service service = new Service(); 094 095 class Service implements ReportRunner { 096 @Override 097 public Set<String> list() { 098 Set<String> names = new HashSet<>(); 099 for (Contribution contrib : configuration) { 100 names.add(contrib.name); 101 } 102 return names; 103 } 104 105 @Override 106 public void run(OutputStream out, Set<String> names) throws IOException { 107 out.write('{'); 108 out.write('"'); 109 out.write(NuxeoInstanceIdentifierHelper.getServerInstanceName().getBytes()); 110 out.write('"'); 111 out.write(':'); 112 out.write('{'); 113 Iterator<Contribution> iterator = configuration.iterator(names); 114 while (iterator.hasNext()) { 115 Contribution contrib = iterator.next(); 116 out.write('"'); 117 out.write(contrib.name.getBytes()); 118 out.write('"'); 119 out.write(':'); 120 contrib.writer.write(out); 121 if (iterator.hasNext()) { 122 out.write(','); 123 } 124 out.flush(); 125 } 126 out.write('}'); 127 out.write('}'); 128 out.flush(); 129 } 130 } 131 132 final Management management = new Management(); 133 134 public class Management implements ReportServer { 135 136 @Override 137 public void run(String host, int port, String... names) throws IOException { 138 ClassLoader tcl = Thread.currentThread().getContextClassLoader(); 139 Thread.currentThread().setContextClassLoader(Management.class.getClassLoader()); 140 try (Socket sock = new Socket(host, port); OutputStream sink = sock.getOutputStream()) { 141 service.run(sink, new HashSet<>(Arrays.asList(names))); 142 } finally { 143 Thread.currentThread().setContextClassLoader(tcl); 144 } 145 } 146 147 } 148 149 @Override 150 public void applicationStarted(ComponentContext context) { 151 instance = this; 152 Framework.getService(ResourcePublisher.class).registerResource("connect-report", "connect-report", 153 ReportServer.class, management); 154 Framework.addListener(new RuntimeServiceListener() { 155 156 @Override 157 public void handleEvent(RuntimeServiceEvent event) { 158 if (event.id != RuntimeServiceEvent.RUNTIME_ABOUT_TO_STOP) { 159 return; 160 } 161 Framework.removeListener(this); 162 applicationStopped(context); 163 } 164 165 }); 166 } 167 168 protected void applicationStopped(ComponentContext context) { 169 Framework.getService(ResourcePublisher.class).unregisterResource("connect-report", "connect-report"); 170 } 171 172 @Override 173 public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 174 if (contribution instanceof Contribution) { 175 configuration.addContribution((Contribution) contribution); 176 } else { 177 throw new IllegalArgumentException( 178 String.format("unknown contribution of type %s in %s", contribution.getClass(), contributor)); 179 } 180 } 181 182 @Override 183 public <T> T getAdapter(Class<T> adapter) { 184 if (adapter.isAssignableFrom(Service.class)) { 185 return adapter.cast(service); 186 } 187 return super.getAdapter(adapter); 188 } 189 190}