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.addListener(new RuntimeServiceListener() { 089 090 @Override 091 public void handleEvent(RuntimeServiceEvent event) { 092 if (RuntimeServiceEvent.RUNTIME_ABOUT_TO_STOP != event.id) { 093 return; 094 } 095 Framework.removeListener(this); 096 Framework.getService(EventService.class).removeListener(ReloadService.RELOAD_TOPIC, reloadListener); 097 } 098 }); 099 Framework.getService(EventService.class).addListener(ReloadService.RELOAD_TOPIC, reloadListener = new ReloadListener(context)); 100 } 101 102 final ReportConfiguration configuration = new ReportConfiguration(); 103 104 final Service service = new Service(); 105 106 class Service implements ReportRunner { 107 @Override 108 public Set<String> list() { 109 Set<String> names = new HashSet<>(); 110 for (Contribution contrib : configuration) { 111 names.add(contrib.name); 112 } 113 return names; 114 } 115 116 @Override 117 public void run(OutputStream out, Set<String> names) throws IOException { 118 out.write('{'); 119 out.write('"'); 120 out.write(NuxeoInstanceIdentifierHelper.getServerInstanceName().getBytes()); 121 out.write('"'); 122 out.write(':'); 123 out.write('{'); 124 Iterator<Contribution> iterator = configuration.iterator(names); 125 while (iterator.hasNext()) { 126 Contribution contrib = iterator.next(); 127 out.write('"'); 128 out.write(contrib.name.getBytes()); 129 out.write('"'); 130 out.write(':'); 131 contrib.writer.write(out); 132 if (iterator.hasNext()) { 133 out.write(','); 134 } 135 out.flush(); 136 } 137 out.write('}'); 138 out.write('}'); 139 out.flush(); 140 } 141 } 142 143 final Management management = new Management(); 144 145 public class Management implements ReportServer { 146 147 @Override 148 public void run(String host, int port, String... names) throws IOException { 149 ClassLoader tcl = Thread.currentThread().getContextClassLoader(); 150 Thread.currentThread().setContextClassLoader(Management.class.getClassLoader()); 151 try (Socket sock = new Socket(host, port)) { 152 try (OutputStream sink = 153 sock.getOutputStream()) { 154 service.run(sink, new HashSet<>(Arrays.asList(names))); 155 } 156 } catch (IOException cause) { 157 throw cause; 158 } finally { 159 Thread.currentThread().setContextClassLoader(tcl); 160 } 161 } 162 163 } 164 165 @Override 166 public void applicationStarted(ComponentContext context) { 167 instance = this; 168 Framework.getService(ResourcePublisher.class).registerResource("connect-report", "connect-report", ReportServer.class, management); 169 Framework.addListener(new RuntimeServiceListener() { 170 171 @Override 172 public void handleEvent(RuntimeServiceEvent event) { 173 if (event.id != RuntimeServiceEvent.RUNTIME_ABOUT_TO_STOP) { 174 return; 175 } 176 Framework.removeListener(this); 177 applicationStopped(context); 178 } 179 180 }); 181 } 182 183 protected void applicationStopped(ComponentContext context) { 184 Framework.getService(ResourcePublisher.class).unregisterResource("connect-report", "connect-report"); 185 } 186 187 @Override 188 public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 189 if (contribution instanceof Contribution) { 190 configuration.addContribution((Contribution) contribution); 191 } else { 192 throw new IllegalArgumentException(String.format("unknown contribution of type %s in %s", contribution.getClass(), contributor)); 193 } 194 } 195 196 @Override 197 public <T> T getAdapter(Class<T> adapter) { 198 if (adapter.isAssignableFrom(Service.class)) { 199 return adapter.cast(service); 200 } 201 return super.getAdapter(adapter); 202 } 203 204}