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.collections.*;
021import org.apache.juneau.serializer.*;
022
023/**
024 * Serializes POJO models to MessagePack.
025 *
026 * <h5 class='section'>Media types:</h5>
027 *
028 * Handles <c>Accept</c> types:  <bc>octal/msgpack</bc>
029 * <p>
030 * Produces <c>Content-Type</c> types: <bc>octal/msgpack</bc>
031 */
032@ConfigurableContext
033public class MsgPackSerializer extends OutputStreamSerializer implements MsgPackMetaProvider, MsgPackCommon {
034
035   //-------------------------------------------------------------------------------------------------------------------
036   // Configurable properties
037   //-------------------------------------------------------------------------------------------------------------------
038
039   static final String PREFIX = "MsgPackSerializer";
040
041   /**
042    * Configuration property:  Add <js>"_type"</js> properties when needed.
043    *
044    * <h5 class='section'>Property:</h5>
045    * <ul class='spaced-list'>
046    *    <li><b>ID:</b>  {@link org.apache.juneau.msgpack.MsgPackSerializer#MSGPACK_addBeanTypes MSGPACK_addBeanTypes}
047    *    <li><b>Name:</b>  <js>"MsgPackSerializer.addBeanTypes.b"</js>
048    *    <li><b>Data type:</b>  <jk>boolean</jk>
049    *    <li><b>System property:</b>  <c>MsgPackSerializer.addBeanTypes</c>
050    *    <li><b>Environment variable:</b>  <c>MSGPACKSERIALIZER_ADDBEANTYPES</c>
051    *    <li><b>Default:</b>  <jk>false</jk>
052    *    <li><b>Session property:</b>  <jk>false</jk>
053    *    <li><b>Annotations:</b>
054    *       <ul>
055    *          <li class='ja'>{@link org.apache.juneau.msgpack.annotation.MsgPackConfig#addBeanTypes()}
056    *       </ul>
057    *    <li><b>Methods:</b>
058    *       <ul>
059    *          <li class='jm'>{@link org.apache.juneau.msgpack.MsgPackSerializerBuilder#addBeanTypes()}
060    *       </ul>
061    * </ul>
062    *
063    * <h5 class='section'>Description:</h5>
064    * <p>
065    * If <jk>true</jk>, then <js>"_type"</js> properties will be added to beans if their type cannot be inferred
066    * through reflection.
067    *
068    * <p>
069    * When present, this value overrides the {@link #SERIALIZER_addBeanTypes} setting and is
070    * provided to customize the behavior of specific serializers in a {@link SerializerGroup}.
071    */
072   public static final String MSGPACK_addBeanTypes = PREFIX + ".addBeanTypes.b";
073
074
075   //-------------------------------------------------------------------------------------------------------------------
076   // Predefined instances
077   //-------------------------------------------------------------------------------------------------------------------
078
079   /** Default serializer, all default settings.*/
080   public static final MsgPackSerializer DEFAULT = new MsgPackSerializer(PropertyStore.DEFAULT);
081
082   /** Default serializer, all default settings, spaced-hex string output.*/
083   public static final MsgPackSerializer DEFAULT_SPACED_HEX = new SpacedHex(PropertyStore.DEFAULT);
084
085   /** Default serializer, all default settings, spaced-hex string output.*/
086   public static final MsgPackSerializer DEFAULT_BASE64 = new Base64(PropertyStore.DEFAULT);
087
088   //-------------------------------------------------------------------------------------------------------------------
089   // Predefined subclasses
090   //-------------------------------------------------------------------------------------------------------------------
091
092   /** Default serializer, spaced-hex string output. */
093   public static class SpacedHex extends MsgPackSerializer {
094
095      /**
096       * Constructor.
097       *
098       * @param ps The property store containing all the settings for this object.
099       */
100      public SpacedHex(PropertyStore ps) {
101         super(
102            ps.builder().setDefault(OSSERIALIZER_binaryFormat, BinaryFormat.SPACED_HEX).build()
103         );
104      }
105   }
106
107   /** Default serializer, BASE64 string output. */
108   public static class Base64 extends MsgPackSerializer {
109
110      /**
111       * Constructor.
112       *
113       * @param ps The property store containing all the settings for this object.
114       */
115      public Base64(PropertyStore ps) {
116         super(
117            ps.builder().setDefault(OSSERIALIZER_binaryFormat, BinaryFormat.BASE64).build()
118         );
119      }
120   }
121
122   //-------------------------------------------------------------------------------------------------------------------
123   // Instance
124   //-------------------------------------------------------------------------------------------------------------------
125
126   private final boolean
127      addBeanTypes;
128   private final Map<ClassMeta<?>,MsgPackClassMeta> msgPackClassMetas = new ConcurrentHashMap<>();
129   private final Map<BeanPropertyMeta,MsgPackBeanPropertyMeta> msgPackBeanPropertyMetas = new ConcurrentHashMap<>();
130
131   /**
132    * Constructor.
133    *
134    * @param ps The property store containing all the settings for this object.
135    */
136   public MsgPackSerializer(PropertyStore ps) {
137      super(ps, "octal/msgpack", null);
138      this.addBeanTypes = getBooleanProperty(MSGPACK_addBeanTypes, getBooleanProperty(SERIALIZER_addBeanTypes, false));
139   }
140
141   @Override /* Context */
142   public MsgPackSerializerBuilder builder() {
143      return new MsgPackSerializerBuilder(getPropertyStore());
144   }
145
146   /**
147    * Instantiates a new clean-slate {@link MsgPackSerializerBuilder} object.
148    *
149    * <p>
150    * This is equivalent to simply calling <code><jk>new</jk> MsgPackSerializerBuilder()</code>.
151    *
152    * <p>
153    * Note that this method creates a builder initialized to all default settings, whereas {@link #builder()} copies
154    * the settings of the object called on.
155    *
156    * @return A new {@link MsgPackSerializerBuilder} object.
157    */
158   public static MsgPackSerializerBuilder create() {
159      return new MsgPackSerializerBuilder();
160   }
161
162   @Override /* Context */
163   public MsgPackSerializerSession createSession() {
164      return createSession(createDefaultSessionArgs());
165   }
166
167   @Override /* Serializer */
168   public MsgPackSerializerSession createSession(SerializerSessionArgs args) {
169      return new MsgPackSerializerSession(this, args);
170   }
171
172   //-----------------------------------------------------------------------------------------------------------------
173   // Extended metadata
174   //-----------------------------------------------------------------------------------------------------------------
175
176   @Override /* MsgPackMetaProvider */
177   public MsgPackClassMeta getMsgPackClassMeta(ClassMeta<?> cm) {
178      MsgPackClassMeta m = msgPackClassMetas.get(cm);
179      if (m == null) {
180         m = new MsgPackClassMeta(cm, this);
181         msgPackClassMetas.put(cm, m);
182      }
183      return m;
184   }
185
186   @Override /* MsgPackMetaProvider */
187   public MsgPackBeanPropertyMeta getMsgPackBeanPropertyMeta(BeanPropertyMeta bpm) {
188      if (bpm == null)
189         return MsgPackBeanPropertyMeta.DEFAULT;
190      MsgPackBeanPropertyMeta m = msgPackBeanPropertyMetas.get(bpm);
191      if (m == null) {
192         m = new MsgPackBeanPropertyMeta(bpm.getDelegateFor(), this);
193         msgPackBeanPropertyMetas.put(bpm, m);
194      }
195      return m;
196   }
197
198   //-----------------------------------------------------------------------------------------------------------------
199   // Properties
200   //-----------------------------------------------------------------------------------------------------------------
201
202   @Override
203   protected final boolean isAddBeanTypes() {
204      return addBeanTypes;
205   }
206
207   //-----------------------------------------------------------------------------------------------------------------
208   // Other methods
209   //-----------------------------------------------------------------------------------------------------------------
210
211   @Override /* Context */
212   public OMap toMap() {
213      return super.toMap()
214         .a("MsgPackSerializer", new DefaultFilteringOMap()
215            .a("addBeanTypes", addBeanTypes)
216         );
217   }
218}