/** * Copyright 2010 CosmoCode GmbH * * Licensed 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 de.cosmocode.palava.ipc.xml.rpc; import java.io.Serializable; import java.util.AbstractList; import java.util.List; import java.util.Map; import javax.annotation.concurrent.ThreadSafe; import javax.xml.bind.JAXBElement; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelHandler.Sharable; import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.handler.codec.oneone.OneToOneDecoder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Preconditions; import com.google.inject.Inject; import de.cosmocode.palava.ipc.IpcArguments; import de.cosmocode.palava.ipc.MapIpcArguments; import de.cosmocode.palava.ipc.xml.rpc.adapters.Adapter; import de.cosmocode.palava.ipc.xml.rpc.generated.MethodCall; import de.cosmocode.palava.ipc.xml.rpc.generated.MethodCall.Params; import de.cosmocode.palava.ipc.xml.rpc.generated.Param; import de.cosmocode.palava.ipc.xml.rpc.generated.Struct; import de.cosmocode.palava.ipc.xml.rpc.generated.Value; /** * A decoder which decodes {@link MethodCall}s into {@link XmlRpcCall}s. * * @since 1.0 * @author Willi Schoenborn */ @Sharable @ThreadSafe final class MethodCallDecoder extends OneToOneDecoder { private static final Logger LOG = LoggerFactory.getLogger(MethodCallDecoder.class); private final Adapter<Value, Map<String, Object>> mapAdapter; private final Adapter<Value, Object> objectAdapter; @Inject public MethodCallDecoder( Adapter<Value, Map<String, Object>> mapAdapter, Adapter<Value, Object> objectAdapter) { this.mapAdapter = Preconditions.checkNotNull(mapAdapter, "MapAdapter"); this.objectAdapter = Preconditions.checkNotNull(objectAdapter, "ObjectAdapter"); } @Override protected Object decode(ChannelHandlerContext context, Channel channel, Object message) throws Exception { if (message instanceof MethodCall) { final MethodCall methodCall = MethodCall.class.cast(message); final String methodName = methodCall.getMethodName(); final Params params = methodCall.getParams(); final IpcArguments arguments; if (params == null) { LOG.trace("No params provided"); arguments = MapIpcArguments.empty(); } else if (params.getParam().isEmpty()) { LOG.trace("Empty params provided"); arguments = MapIpcArguments.empty(); } else if (hasSingleStruct(params)) { LOG.trace("Treating single struct param as named params"); // single struct parameter will be treated as named parameters arguments = named(params.getParam().get(0).getValue()); } else { LOG.trace("Treating params as positional"); // positional parameters arguments = positional(params); } return new XmlRpcCall(methodName, arguments); } else { return message; } } private boolean hasSingleStruct(Params params) { if (params.getParam().size() == 1) { final Serializable first = params.getParam().get(0).getValue().getContent().get(0); if (first instanceof JAXBElement<?>) { return JAXBElement.class.cast(first).getValue() instanceof Struct; } } return false; } private IpcArguments named(Value value) { return new XmlRpcArguments(mapAdapter.decode(value)); } private IpcArguments positional(Params params) { return new XmlRpcArguments(new ParamsList(params.getParam(), objectAdapter)); } /** * {@link List} of {@link Param} backed {@link List} implementation. * * @since 1.0 * @author Willi Schoenborn */ private static final class ParamsList extends AbstractList<Object> { private final List<Param> params; private final Adapter<Value, Object> objectAdapter; public ParamsList(List<Param> params, Adapter<Value, Object> objectAdapter) { this.params = params; this.objectAdapter = objectAdapter; } @Override public Object get(int index) { return objectAdapter.decode(params.get(index).getValue()); } @Override public int size() { return params.size(); } } }