/*
* Copyright (c) 2009 - 2016 Deutsches Elektronen-Synchroton,
* Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program (see the file COPYING.LIB for more
* details); if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.dcache.xdr;
import javax.security.auth.Subject;
import java.io.IOException;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import com.google.common.base.Throwables;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.glassfish.grizzly.filterchain.BaseFilter;
import org.glassfish.grizzly.filterchain.FilterChainContext;
import org.glassfish.grizzly.filterchain.NextAction;
import static java.util.Objects.requireNonNull;
public class RpcDispatcher extends BaseFilter {
private final static Logger _log = LoggerFactory.getLogger(RpcDispatcher.class);
/**
* List of registered RPC services
*
*/
private final Map<OncRpcProgram, RpcDispatchable> _programs;
/**
* {@link ExecutorService} used for request processing
*/
private final ExecutorService _asyncExecutorService;
/**
* If {@code true}, then request will be performed as {@link Subject} created
* from request credentials.
*/
private final boolean _withSubjectPropagation;
/**
* Create new RPC dispatcher for given program.
*
* @param executor {@link ExecutorService} to use for request processing
* @param programs {@link Map}
* with a mapping between program number and program
* handler.
* @param withSubjectPropagation use {@link Subject#doAs} to exacerbate request.
*
* @throws NullPointerException if executor or program is null
*/
public RpcDispatcher(ExecutorService executor, Map<OncRpcProgram,
RpcDispatchable> programs, boolean withSubjectPropagation)
throws NullPointerException {
_programs = requireNonNull(programs, "Programs is NULL");
_asyncExecutorService = requireNonNull(executor, "ExecutorService is NULL");
_withSubjectPropagation = withSubjectPropagation;
}
@Override
public NextAction handleRead(final FilterChainContext ctx) throws IOException {
final RpcCall call = ctx.getMessage();
final int prog = call.getProgram();
final int vers = call.getProgramVersion();
final int proc = call.getProcedure();
_log.debug("processing request {}", call);
final RpcDispatchable program = _programs.get(new OncRpcProgram(prog, vers));
if (program == null) {
call.failProgramUnavailable();
} else {
_asyncExecutorService.execute(new Runnable() {
@Override
public void run() {
try {
if (_withSubjectPropagation) {
Subject subject = call.getCredential().getSubject();
try {
Subject.doAs(subject, (PrivilegedExceptionAction<Void>) () -> {
program.dispatchOncRpcCall(call);
return null;
});
} catch (PrivilegedActionException e) {
Throwable t = e.getCause();
Throwables.throwIfInstanceOf(t, IOException.class);
Throwables.throwIfUnchecked(t);
throw new RuntimeException("Unexpected exception", e);
}
} else {
program.dispatchOncRpcCall(call);
}
} catch (RpcException e) {
call.reject(e.getStatus(), e.getRpcReply());
_log.warn("Failed to process RPC request: {}", e.getMessage());
} catch (OncRpcException e) {
call.failRpcGarbage();
_log.warn("Failed to process RPC request: {}", e.getMessage());
} catch (IOException e) {
call.failRpcGarbage();
_log.warn("Failed to process RPC request: {}", e.getMessage());
} catch (RuntimeException e) {
/*
* This looks like a bug in dispatcher implementation.
* Log the error and tell client that we fail.
*/
_log.error("Failed to process RPC request:", e);
call.failRpcSystem();
throw e;
}
}
@Override
public String toString() {
return call.toString();
}
});
}
return ctx.getInvokeAction();
}
}