001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.rest.converter;
018
019import static org.apache.juneau.commons.utils.ThrowableUtils.*;
020import static org.apache.juneau.commons.utils.Utils.*;
021
022import org.apache.juneau.*;
023import org.apache.juneau.http.response.*;
024import org.apache.juneau.json.*;
025import org.apache.juneau.objecttools.*;
026import org.apache.juneau.rest.*;
027
028/**
029 * Converter for enablement of {@link ObjectIntrospector} support on response objects returned by a
030 * <c>@RestOp</c>-annotated method.
031 *
032 * <p>
033 * When enabled, public methods can be called on objects returned through the {@link RestResponse#setContent(Object)}
034 * method.
035 *
036 * <p>
037 * Note that opening up public methods for calling through a REST interface can be dangerous, and should be done with
038 * caution.
039 *
040 * <p>
041 * Java methods are invoked by passing in the following URL parameters:
042 * <ul class='spaced-list'>
043 *    <li>
044 *       <c>&amp;invokeMethod</c> - The Java method name, optionally with arguments if necessary to
045 *       differentiate between methods.
046 *    <li>
047 *       <c>&amp;invokeArgs</c> - The arguments as an array.
048 * </ul>
049 *
050 * <h5 class='section'>See Also:</h5><ul>
051 *    <li class='jc'>{@link ObjectIntrospector} - Additional information on introspection of POJO methods.
052 *    <li class='jm'>{@link org.apache.juneau.rest.RestOpContext.Builder#converters()} - Registering converters with REST resources.
053 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/Converters">Converters</a>
054 * </ul>
055 */
056public class Introspectable implements RestConverter {
057
058   /**
059    * Swagger parameters for this converter.
060    */
061   public static final String SWAGGER_PARAMS = """
062      {in:'query',name:'invokeMethod',description:' The Java method name, optionally with arguments if necessary to differentiate between methods.',examples:{example:'toString'}},
063      {in:'query',name:'invokeArgs',description:'The arguments as an array.',examples:{example:'foo,bar'}}
064      """;
065
066   @Override /* Overridden from RestConverter */
067   public Object convert(RestRequest req, Object o) throws InternalServerError {
068      String method = req.getQueryParam("invokeMethod").orElse(null);
069      String args = req.getQueryParam("invokeArgs").orElse(null);
070      if (method == null)
071         return o;
072      try {
073         BeanSession bs = req.getBeanSession();
074         var swap = bs.getClassMetaForObject(o).getSwap(bs);
075         if (nn(swap))
076            o = swap.swap(bs, o);
077         return ObjectIntrospector.create(o, JsonParser.DEFAULT).invokeMethod(method, args);
078      } catch (Exception e) {
079         return new InternalServerError(e, "Error occurred trying to invoke method: {0}", lm(e));
080      }
081   }
082}