001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.msgpack;
014
015import java.util.*;
016import java.util.concurrent.*;
017
018import org.apache.juneau.*;
019import org.apache.juneau.annotation.*;
020import org.apache.juneau.serializer.*;
021
022/**
023 * Serializes POJO models to MessagePack.
024 *
025 * <h5 class='section'>Media types:</h5>
026 *
027 * Handles <c>Accept</c> types:  <bc>octal/msgpack</bc>
028 * <p>
029 * Produces <c>Content-Type</c> types: <bc>octal/msgpack</bc>
030 */
031@ConfigurableContext
032public class MsgPackSerializer extends OutputStreamSerializer implements MsgPackMetaProvider, MsgPackCommon {
033
034   //-------------------------------------------------------------------------------------------------------------------
035   // Configurable properties
036   //-------------------------------------------------------------------------------------------------------------------
037
038   static final String PREFIX = "MsgPackSerializer";
039
040   /**
041    * Configuration property:  Add <js>"_type"</js> properties when needed.
042    *
043    * <h5 class='section'>Property:</h5>
044    * <ul class='spaced-list'>
045    *    <li><b>ID:</b>  {@link org.apache.juneau.msgpack.MsgPackSerializer#MSGPACK_addBeanTypes MSGPACK_addBeanTypes}
046    *    <li><b>Name:</b>  <js>"MsgPackSerializer.addBeanTypes.b"</js>
047    *    <li><b>Data type:</b>  <jk>boolean</jk>
048    *    <li><b>System property:</b>  <c>MsgPackSerializer.addBeanTypes</c>
049    *    <li><b>Environment variable:</b>  <c>MSGPACKSERIALIZER_ADDBEANTYPES</c>
050    *    <li><b>Default:</b>  <jk>false</jk>
051    *    <li><b>Session property:</b>  <jk>false</jk>
052    *    <li><b>Annotations:</b>
053    *       <ul>
054    *          <li class='ja'>{@link org.apache.juneau.msgpack.annotation.MsgPackConfig#addBeanTypes()}
055    *       </ul>
056    *    <li><b>Methods:</b>
057    *       <ul>
058    *          <li class='jm'>{@link org.apache.juneau.msgpack.MsgPackSerializerBuilder#addBeanTypes(boolean)}
059    *       </ul>
060    * </ul>
061    *
062    * <h5 class='section'>Description:</h5>
063    * <p>
064    * If <jk>true</jk>, then <js>"_type"</js> properties will be added to beans if their type cannot be inferred
065    * through reflection.
066    *
067    * <p>
068    * When present, this value overrides the {@link #SERIALIZER_addBeanTypes} setting and is
069    * provided to customize the behavior of specific serializers in a {@link SerializerGroup}.
070    */
071   public static final String MSGPACK_addBeanTypes = PREFIX + ".addBeanTypes.b";
072
073
074   //-------------------------------------------------------------------------------------------------------------------
075   // Predefined instances
076   //-------------------------------------------------------------------------------------------------------------------
077
078   /** Default serializer, all default settings.*/
079   public static final MsgPackSerializer DEFAULT = new MsgPackSerializer(PropertyStore.DEFAULT);
080
081   /** Default serializer, all default settings, spaced-hex string output.*/
082   public static final MsgPackSerializer DEFAULT_SPACED_HEX = new SpacedHex(PropertyStore.DEFAULT);
083
084   /** Default serializer, all default settings, spaced-hex string output.*/
085   public static final MsgPackSerializer DEFAULT_BASE64 = new Base64(PropertyStore.DEFAULT);
086
087   //-------------------------------------------------------------------------------------------------------------------
088   // Predefined subclasses
089   //-------------------------------------------------------------------------------------------------------------------
090
091   /** Default serializer, spaced-hex string output. */
092   public static class SpacedHex extends MsgPackSerializer {
093
094      /**
095       * Constructor.
096       *
097       * @param ps The property store containing all the settings for this object.
098       */
099      public SpacedHex(PropertyStore ps) {
100         super(
101            ps.builder().set(OSSERIALIZER_binaryFormat, BinaryFormat.SPACED_HEX).build()
102         );
103      }
104   }
105
106   /** Default serializer, BASE64 string output. */
107   public static class Base64 extends MsgPackSerializer {
108
109      /**
110       * Constructor.
111       *
112       * @param ps The property store containing all the settings for this object.
113       */
114      public Base64(PropertyStore ps) {
115         super(
116            ps.builder().set(OSSERIALIZER_binaryFormat, BinaryFormat.BASE64).build()
117         );
118      }
119   }
120
121   //-------------------------------------------------------------------------------------------------------------------
122   // Instance
123   //-------------------------------------------------------------------------------------------------------------------
124
125   private final boolean
126      addBeanTypes;
127   private final Map<ClassMeta<?>,MsgPackClassMeta> msgPackClassMetas = new ConcurrentHashMap<>();
128   private final Map<BeanPropertyMeta,MsgPackBeanPropertyMeta> msgPackBeanPropertyMetas = new ConcurrentHashMap<>();
129
130   /**
131    * Constructor.
132    *
133    * @param ps The property store containing all the settings for this object.
134    */
135   public MsgPackSerializer(PropertyStore ps) {
136      super(ps, "octal/msgpack", null);
137      this.addBeanTypes = getBooleanProperty(MSGPACK_addBeanTypes, getBooleanProperty(SERIALIZER_addBeanTypes, false));
138   }
139
140   @Override /* Context */
141   public MsgPackSerializerBuilder builder() {
142      return new MsgPackSerializerBuilder(getPropertyStore());
143   }
144
145   /**
146    * Instantiates a new clean-slate {@link MsgPackSerializerBuilder} object.
147    *
148    * <p>
149    * This is equivalent to simply calling <code><jk>new</jk> MsgPackSerializerBuilder()</code>.
150    *
151    * <p>
152    * Note that this method creates a builder initialized to all default settings, whereas {@link #builder()} copies
153    * the settings of the object called on.
154    *
155    * @return A new {@link MsgPackSerializerBuilder} object.
156    */
157   public static MsgPackSerializerBuilder create() {
158      return new MsgPackSerializerBuilder();
159   }
160
161   @Override /* Context */
162   public MsgPackSerializerSession createSession() {
163      return createSession(createDefaultSessionArgs());
164   }
165
166   @Override /* Serializer */
167   public MsgPackSerializerSession createSession(SerializerSessionArgs args) {
168      return new MsgPackSerializerSession(this, args);
169   }
170
171   //-----------------------------------------------------------------------------------------------------------------
172   // Extended metadata
173   //-----------------------------------------------------------------------------------------------------------------
174
175   @Override /* MsgPackMetaProvider */
176   public MsgPackClassMeta getMsgPackClassMeta(ClassMeta<?> cm) {
177      MsgPackClassMeta m = msgPackClassMetas.get(cm);
178      if (m == null) {
179         m = new MsgPackClassMeta(cm, this);
180         msgPackClassMetas.put(cm, m);
181      }
182      return m;
183   }
184
185   @Override /* MsgPackMetaProvider */
186   public MsgPackBeanPropertyMeta getMsgPackBeanPropertyMeta(BeanPropertyMeta bpm) {
187      if (bpm == null)
188         return MsgPackBeanPropertyMeta.DEFAULT;
189      MsgPackBeanPropertyMeta m = msgPackBeanPropertyMetas.get(bpm);
190      if (m == null) {
191         m = new MsgPackBeanPropertyMeta(bpm.getDelegateFor(), this);
192         msgPackBeanPropertyMetas.put(bpm, m);
193      }
194      return m;
195   }
196
197   //-----------------------------------------------------------------------------------------------------------------
198   // Properties
199   //-----------------------------------------------------------------------------------------------------------------
200
201   @Override
202   protected final boolean isAddBeanTypes() {
203      return addBeanTypes;
204   }
205
206   //-----------------------------------------------------------------------------------------------------------------
207   // Other methods
208   //-----------------------------------------------------------------------------------------------------------------
209
210   @Override /* Context */
211   public ObjectMap toMap() {
212      return super.toMap()
213         .append("MsgPackSerializer", new DefaultFilteringObjectMap()
214            .append("addBeanTypes", addBeanTypes)
215         );
216   }
217}