/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.ignite.internal.client.router.impl; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.UUID; import org.apache.ignite.IgniteLogger; import org.apache.ignite.internal.client.GridClientException; import org.apache.ignite.internal.client.GridClientFuture; import org.apache.ignite.internal.client.GridClientFutureListener; import org.apache.ignite.internal.client.marshaller.GridClientMarshaller; import org.apache.ignite.internal.client.marshaller.jdk.GridClientJdkMarshaller; import org.apache.ignite.internal.client.marshaller.optimized.GridClientOptimizedMarshaller; import org.apache.ignite.internal.client.marshaller.optimized.GridClientZipOptimizedMarshaller; import org.apache.ignite.internal.processors.rest.client.message.GridClientHandshakeRequest; import org.apache.ignite.internal.processors.rest.client.message.GridClientHandshakeResponse; import org.apache.ignite.internal.processors.rest.client.message.GridClientMessage; import org.apache.ignite.internal.processors.rest.client.message.GridClientPingPacket; import org.apache.ignite.internal.processors.rest.client.message.GridClientResponse; import org.apache.ignite.internal.processors.rest.client.message.GridRouterRequest; import org.apache.ignite.internal.processors.rest.client.message.GridRouterResponse; import org.apache.ignite.internal.util.nio.GridNioServerListener; import org.apache.ignite.internal.util.nio.GridNioSession; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.plugin.PluginProvider; import org.jetbrains.annotations.Nullable; import static org.apache.ignite.internal.util.nio.GridNioSessionMetaKey.MARSHALLER; import static org.apache.ignite.internal.util.nio.GridNioSessionMetaKey.MARSHALLER_ID; /** * Nio listener for the router. Extracts necessary meta information from messages * and delegates their delivery to underlying client. */ public abstract class GridTcpRouterNioListenerAdapter implements GridNioServerListener<GridClientMessage> { /** Supported protocol versions. */ private static final Collection<Short> SUPP_VERS = new HashSet<>(); /** */ static { SUPP_VERS.add((short)1); } /** Logger. */ private final IgniteLogger log; /** Client for grid access. */ private final GridRouterClientImpl client; /** Marshallers map. */ protected final Map<Byte, GridClientMarshaller> marshMap; /** * @param log Logger. * @param client Client for grid access. */ @SuppressWarnings({"AbstractMethodCallInConstructor", "OverriddenMethodCallDuringObjectConstruction"}) public GridTcpRouterNioListenerAdapter(IgniteLogger log, GridRouterClientImpl client) { this.log = log; this.client = client; marshMap = new HashMap<>(); List<PluginProvider> providers = U.allPluginProviders(); GridClientOptimizedMarshaller optdMarsh = new GridClientOptimizedMarshaller(providers); marshMap.put(GridClientOptimizedMarshaller.ID, optdMarsh); marshMap.put(GridClientZipOptimizedMarshaller.ID, new GridClientZipOptimizedMarshaller(optdMarsh, providers)); marshMap.put(GridClientJdkMarshaller.ID, new GridClientJdkMarshaller()); init(); } /** */ protected abstract void init(); /** {@inheritDoc} */ @Override public void onConnected(GridNioSession ses) { // No-op. } /** {@inheritDoc} */ @Override public void onDisconnected(GridNioSession ses, @Nullable Exception e) { if (e != null) { if (e instanceof RuntimeException) U.error(log, "Failed to process request from remote client: " + ses, e); else U.warn(log, "Closed client session due to exception [ses=" + ses + ", err=" + e.getMessage() + ']'); } } /** {@inheritDoc} */ @SuppressWarnings("TypeMayBeWeakened") @Override public void onMessage(final GridNioSession ses, final GridClientMessage msg) { if (msg instanceof GridRouterRequest) { GridRouterRequest routerMsg = (GridRouterRequest)msg; final UUID clientId = routerMsg.clientId(); final long reqId = routerMsg.requestId(); try { client.forwardMessage(routerMsg, routerMsg.destinationId(), ses.<Byte>meta(MARSHALLER_ID.ordinal())) .listen(new GridClientFutureListener() { @Override public void onDone(GridClientFuture fut) { try { GridRouterResponse res = (GridRouterResponse)fut.get(); // Restoring original request id, because it was overwritten by the client. res.requestId(reqId); ses.send(res); } catch (GridClientException e) { ses.send(makeFailureResponse(e, clientId, reqId)); } } }); } catch (GridClientException e) { ses.send(makeFailureResponse(e, clientId, reqId)); } catch (InterruptedException e) { Thread.currentThread().interrupt(); U.warn( log, "Message forwarding was interrupted (will ignore last message): " + e.getMessage(), "Message forwarding was interrupted."); } } else if (msg instanceof GridClientHandshakeRequest) { GridClientHandshakeRequest hs = (GridClientHandshakeRequest)msg; short ver = hs.version(); if (!SUPP_VERS.contains(ver)) { U.error(log, "Client protocol version is not supported [ses=" + ses + ", ver=" + ver + ", supported=" + SUPP_VERS + ']'); ses.close(); } else { byte marshId = hs.marshallerId(); GridClientMarshaller marsh = marshMap.get(marshId); if (marsh == null) { U.error(log, "Client marshaller ID is invalid. Note that .NET and C++ clients " + "are supported only in enterprise edition [ses=" + ses + ", marshId=" + marshId + ']'); ses.close(); } else { ses.addMeta(MARSHALLER_ID.ordinal(), marshId); ses.addMeta(MARSHALLER.ordinal(), marsh); ses.send(GridClientHandshakeResponse.OK); } } } else if (msg instanceof GridClientPingPacket) ses.send(GridClientPingPacket.PING_MESSAGE); else throw new IllegalArgumentException("Unsupported input message: " + msg); } /** {@inheritDoc} */ @Override public void onSessionWriteTimeout(GridNioSession ses) { U.warn(log, "Closing NIO session because of write timeout."); ses.close(); } /** {@inheritDoc} */ @Override public void onSessionIdleTimeout(GridNioSession ses) { U.warn(log, "Closing NIO session because of idle."); ses.close(); } /** * Creates a failure response, based on the given exception. * * @param e Exception to extract failure report from. * @param clientId Client id. * @param reqId Request id. * @return Failure response. */ private GridClientResponse makeFailureResponse(GridClientException e, UUID clientId, Long reqId) { U.error(log, "Failed to process message on router.", e); GridClientResponse res = new GridClientResponse(); res.clientId(clientId); res.requestId(reqId); res.successStatus(GridClientResponse.STATUS_FAILED); res.errorMessage("Failed to process message on router " + "[exception=" + e.getClass().getSimpleName() + ", message=" + e.getMessage() + ']'); return res; } }