Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.deephaven.server.appmode.ApplicationTicketResolver Maven / Gradle / Ivy
//
// Copyright (c) 2016-2024 Deephaven Data Labs and Patent Pending
//
package io.deephaven.server.appmode;
import com.google.protobuf.ByteStringAccess;
import com.google.rpc.Code;
import io.deephaven.appmode.ApplicationState;
import io.deephaven.appmode.Field;
import io.deephaven.base.string.EncodingInfo;
import io.deephaven.engine.table.Table;
import io.deephaven.proto.backplane.grpc.Ticket;
import io.deephaven.proto.flight.util.TicketRouterHelper;
import io.deephaven.proto.util.ApplicationTicketHelper;
import io.deephaven.proto.util.Exceptions;
import io.deephaven.server.auth.AuthorizationProvider;
import io.deephaven.server.session.SessionState;
import io.deephaven.server.session.TicketResolverBase;
import io.deephaven.server.session.TicketRouter;
import io.grpc.StatusRuntimeException;
import org.apache.arrow.flight.impl.Flight;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.CharsetDecoder;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import static io.deephaven.proto.util.ApplicationTicketHelper.TICKET_PREFIX;
import static io.deephaven.proto.util.ApplicationTicketHelper.FLIGHT_DESCRIPTOR_ROUTE;
@Singleton
public class ApplicationTicketResolver extends TicketResolverBase implements ApplicationStates {
private final Map applicationMap = new ConcurrentHashMap<>();
@Inject
public ApplicationTicketResolver(final AuthorizationProvider authProvider) {
super(authProvider, (byte) TICKET_PREFIX, FLIGHT_DESCRIPTOR_ROUTE);
}
@Override
public final Optional getApplicationState(String applicationId) {
return Optional.ofNullable(applicationMap.get(applicationId));
}
public synchronized void onApplicationLoad(final ApplicationState app) {
if (applicationMap.containsKey(app.id())) {
if (applicationMap.get(app.id()) != app) {
throw new IllegalArgumentException("Duplicate application found for app_id " + app.id());
}
return;
}
applicationMap.put(app.id(), app);
}
@Override
public SessionState.ExportObject resolve(
@Nullable final SessionState session, final ByteBuffer ticket, final String logId) {
return resolve(appFieldIdFor(ticket, logId), logId);
}
@Override
public SessionState.ExportObject resolve(
@Nullable final SessionState session, final Flight.FlightDescriptor descriptor, final String logId) {
return resolve(appFieldIdFor(descriptor, logId), logId);
}
private SessionState.ExportObject resolve(final AppFieldId id, final String logId) {
if (id.app == null) {
throw Exceptions.statusRuntimeException(Code.FAILED_PRECONDITION,
"Could not resolve '" + logId + "': field '" + getLogNameFor(id)
+ "' does not belong to an application");
}
final Field field = id.app.getField(id.fieldName);
if (field == null) {
throw newNotFoundSRE(logId, id);
}
Object value = authorization.transform(field.value());
if (value == null) {
throw newNotFoundSRE(logId, id);
}
// noinspection unchecked
return SessionState.wrapAsExport((T) value);
}
@Override
public SessionState.ExportObject flightInfoFor(
@Nullable final SessionState session, final Flight.FlightDescriptor descriptor, final String logId) {
final AppFieldId id = appFieldIdFor(descriptor, logId);
if (id.app == null) {
throw Exceptions.statusRuntimeException(Code.FAILED_PRECONDITION,
"Could not resolve '" + logId + "': field does not belong to an application");
}
final Flight.FlightInfo info;
synchronized (id.app) {
Field> field = id.app.getField(id.fieldName);
if (field == null) {
throw newNotFoundSRE(logId, id);
}
Object value = field.value();
if (value instanceof Table) {
// may return null if the table is not authorized
value = authorization.transform(value);
}
if (value instanceof Table) {
info = TicketRouter.getFlightInfo((Table) value, descriptor, flightTicketForName(id.app, id.fieldName));
} else {
throw newNotFoundSRE(logId, id);
}
}
return SessionState.wrapAsExport(info);
}
@Override
public SessionState.ExportBuilder publish(
final SessionState session,
final ByteBuffer ticket,
final String logId,
final Runnable onPublish) {
throw Exceptions.statusRuntimeException(Code.FAILED_PRECONDITION,
"Could not publish '" + logId + "': application tickets cannot be published to");
}
@Override
public SessionState.ExportBuilder publish(
final SessionState session,
final Flight.FlightDescriptor descriptor,
final String logId,
final Runnable onPublish) {
throw Exceptions.statusRuntimeException(Code.FAILED_PRECONDITION,
"Could not publish '" + logId + "': application flight descriptors cannot be published to");
}
@Override
public String getLogNameFor(final ByteBuffer ticket, final String logId) {
return getLogNameFor(appFieldIdFor(ticket, logId));
}
private String getLogNameFor(final AppFieldId id) {
return "app/" + id.applicationId() + "/field/" + id.fieldName;
}
@Override
public void forAllFlightInfo(@Nullable SessionState session, Consumer visitor) {
applicationMap.values().forEach(app -> {
app.listFields().forEach(field -> {
Object value = field.value();
if (value instanceof Table) {
// may return null if the table is not authorized
value = authorization.transform(value);
}
if (value instanceof Table) {
final Flight.FlightInfo info = TicketRouter.getFlightInfo((Table) value,
descriptorForName(app, field.name()), flightTicketForName(app, field.name()));
visitor.accept(info);
}
});
});
}
/**
* Convenience method to convert from an application variable name to Ticket
*
* @param app the application state that this field is defined in
* @param name the application variable name to convert
* @return the ticket this descriptor represents
*/
public static Ticket ticketForName(final ApplicationState app, final String name) {
final byte[] ticket = ApplicationTicketHelper.applicationFieldToBytes(app.id(), name);
return Ticket.newBuilder()
.setTicket(ByteStringAccess.wrap(ticket))
.build();
}
/**
* Convenience method to convert from an application variable name to Flight.Ticket
*
* @param app the application state that this field is defined in
* @param name the application variable name to convert
* @return the ticket this descriptor represents
*/
public static Flight.Ticket flightTicketForName(final ApplicationState app, final String name) {
final byte[] ticket = ApplicationTicketHelper.applicationFieldToBytes(app.id(), name);
return Flight.Ticket.newBuilder()
.setTicket(ByteStringAccess.wrap(ticket))
.build();
}
/**
* Convenience method to convert from a scoped variable name to Flight.FlightDescriptor
*
* @param app the application state that this field is defined in
* @param name the application variable name to convert
* @return the flight descriptor this descriptor represents
*/
public static Flight.FlightDescriptor descriptorForName(final ApplicationState app, final String name) {
return Flight.FlightDescriptor.newBuilder()
.setType(Flight.FlightDescriptor.DescriptorType.PATH)
.addAllPath(ApplicationTicketHelper.applicationFieldToPath(app.id(), name))
.build();
}
private @NotNull StatusRuntimeException newNotFoundSRE(String logId, AppFieldId id) {
return Exceptions.statusRuntimeException(Code.NOT_FOUND,
"Could not resolve '" + logId + "': field '" + getLogNameFor(id) + "' not found");
}
private AppFieldId appFieldIdFor(final ByteBuffer ticket, final String logId) {
if (ticket == null) {
throw Exceptions.statusRuntimeException(Code.FAILED_PRECONDITION,
"Could not resolve '" + logId + "': ticket not supplied");
}
final String ticketAsString;
final int initialLimit = ticket.limit();
final int initialPosition = ticket.position();
final CharsetDecoder decoder = EncodingInfo.UTF_8.getDecoder().reset();
try {
ticketAsString = decoder.decode(ticket).toString();
} catch (CharacterCodingException e) {
throw Exceptions.statusRuntimeException(Code.FAILED_PRECONDITION,
"Could not resolve '" + logId + "': failed to decode: " + e.getMessage());
} finally {
ticket.position(initialPosition);
ticket.limit(initialLimit);
}
final int endOfRoute = ticketAsString.indexOf('/');
final int endOfAppId = ticketAsString.indexOf('/', endOfRoute + 1);
final int endOfFieldSegment = ticketAsString.indexOf('/', endOfAppId + 1);
if (endOfAppId == -1 || endOfFieldSegment == -1) {
throw Exceptions.statusRuntimeException(Code.FAILED_PRECONDITION,
"Could not resolve '" + logId + "': ticket does conform to expected format");
}
final String appId = ticketAsString.substring(endOfRoute + 1, endOfAppId);
final String fieldName = ticketAsString.substring(endOfFieldSegment + 1);
final ApplicationState app = applicationMap.get(appId);
if (app == null) {
throw Exceptions.statusRuntimeException(Code.NOT_FOUND,
"Could not resolve '" + logId + "': no application exists with the identifier: " + appId);
}
return AppFieldId.from(app, fieldName);
}
private AppFieldId appFieldIdFor(final Flight.FlightDescriptor descriptor, final String logId) {
if (descriptor == null) {
throw Exceptions.statusRuntimeException(Code.FAILED_PRECONDITION,
"Could not resolve '" + logId + "': descriptor not supplied");
}
if (descriptor.getType() != Flight.FlightDescriptor.DescriptorType.PATH) {
throw Exceptions.statusRuntimeException(Code.FAILED_PRECONDITION,
"Could not resolve '" + logId + "': only flight paths are supported");
}
// current structure: a/app_id/f/field_name
if (descriptor.getPathCount() != 4) {
throw Exceptions.statusRuntimeException(Code.FAILED_PRECONDITION,
"Could not resolve '" + logId + "': unexpected path length (found: "
+ TicketRouterHelper.getLogNameFor(descriptor) + ", expected: 4)");
}
final String appId = descriptor.getPath(1);
final ApplicationState app = applicationMap.get(appId);
if (app == null) {
throw Exceptions.statusRuntimeException(Code.NOT_FOUND,
"Could not resolve '" + logId + "': no application exists with the identifier: " + appId);
}
if (!descriptor.getPath(2).equals(ApplicationTicketHelper.FIELD_PATH_SEGMENT)) {
throw Exceptions.statusRuntimeException(Code.NOT_FOUND,
"Could not resolve '" + logId + "': path is not an application field");
}
return AppFieldId.from(app, descriptor.getPath(3));
}
}