package org.yamcs.web.websocket;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import org.slf4j.Logger;
import org.yamcs.ProcessorClient;
import org.yamcs.ProcessorException;
import org.yamcs.Processor;
import org.yamcs.management.ManagementService;
import org.yamcs.security.AuthenticationToken;
import org.yamcs.security.Privilege;
import org.yamcs.utils.LoggingUtils;
/**
* Runs on the server side and oversees the life cycle of a client web socket connection to a Processor. Combines multiple types of subscriptions
* to keep them bundled as one client session.
*/
public class WebSocketProcessorClient implements ProcessorClient {
private final Logger log;
private final int clientId;
private final String applicationName;
private final String username;
private final AuthenticationToken authToken;
private Processor processor;
private List<AbstractWebSocketResource> resources = new CopyOnWriteArrayList<>();
private WebSocketFrameHandler wsHandler;
public WebSocketProcessorClient(String yamcsInstance, WebSocketFrameHandler wsHandler, String applicationName, AuthenticationToken authToken) {
this.applicationName = applicationName;
this.authToken = authToken;
this.username = authToken != null ? authToken.getPrincipal().toString() : Privilege.getDefaultUser();
this.wsHandler = wsHandler;
log = LoggingUtils.getLogger(WebSocketProcessorClient.class, yamcsInstance);
processor = Processor.getFirstProcessor(yamcsInstance);
clientId = ManagementService.getInstance().registerClient(yamcsInstance, processor.getName(), this);
// Built-in resources, we could consider moving this to services so that
// they register their endpoint themselves.
registerResource(ParameterResource.RESOURCE_NAME, new ParameterResource(this));
registerResource(CommandHistoryResource.RESOURCE_NAME, new CommandHistoryResource(this));
registerResource(ManagementResource.RESOURCE_NAME, new ManagementResource(this));
registerResource(AlarmResource.RESOURCE_NAME, new AlarmResource(this));
registerResource(EventResource.RESOURCE_NAME, new EventResource(this));
registerResource(StreamResource.RESOURCE_NAME, new StreamResource(this));
registerResource(TimeResource.RESOURCE_NAME, new TimeResource(this));
registerResource(LinkResource.RESOURCE_NAME, new LinkResource(this));
registerResource(CommandQueueResource.RESOURCE_NAME, new CommandQueueResource(this));
registerResource(PacketResource.RESOURCE_NAME, new PacketResource(this));
}
@Override
public void switchProcessor(Processor newProcessor, AuthenticationToken authToken) throws ProcessorException {
log.info("Switching processor from {}/{} to {}/{}", processor.getInstance(), processor.getName(), newProcessor.getInstance(), newProcessor.getName());
Processor oldProcessor = processor;
processor = newProcessor;
for (AbstractWebSocketResource resource : resources) {
resource.switchProcessor(oldProcessor, newProcessor);
}
// Note: We're not updating log and clientId in case of instance change. Maybe that's something we should do though
}
public Processor getProcessor() {
return processor;
}
public void registerResource(String route, AbstractWebSocketResource resource) {
wsHandler.addResource(route, resource);
resources.add(resource);
}
public int getClientId() {
return clientId;
}
public AuthenticationToken getAuthToken() {
return authToken;
}
public WebSocketFrameHandler getWebSocketFrameHandler() {
return wsHandler;
}
@Override
public void processorQuit() {
}
@Override
public String getUsername() {
return username;
}
@Override
public String getApplicationName() {
return applicationName;
}
/**
* Called when the socket is closed.
*/
public void quit() {
ManagementService.getInstance().unregisterClient(clientId);
resources.forEach(r -> r.quit());
}
}