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