001/* 
002    Licensed to the Apache Software Foundation (ASF) under one
003    or more contributor license agreements.  See the NOTICE file
004    distributed with this work for additional information
005    regarding copyright ownership.  The ASF licenses this file
006    to you under the Apache License, Version 2.0 (the
007    "License"); you may not use this file except in compliance
008    with the License.  You may obtain a copy of the License at
009
010       http://www.apache.org/licenses/LICENSE-2.0
011
012    Unless required by applicable law or agreed to in writing,
013    software distributed under the License is distributed on an
014    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015    KIND, either express or implied.  See the License for the
016    specific language governing permissions and limitations
017    under the License.  
018 */
019package org.apache.wiki.util;
020
021import java.io.ByteArrayInputStream;
022import java.io.ByteArrayOutputStream;
023import java.io.IOException;
024import java.io.InputStream;
025import java.io.ObjectInputStream;
026import java.io.ObjectOutputStream;
027import java.io.Serializable;
028import java.nio.charset.StandardCharsets;
029import java.util.Base64;
030import java.util.HashMap;
031import java.util.Map;
032
033
034/**
035 *  Provides static helper functions for serializing different objects.
036 *  
037 *  @since 2.8
038 */
039public final class Serializer
040{
041    /**
042     * Prefix used to indicated that a serialized item was encoded with Base64.
043     */
044    protected static final String BASE64_PREFIX = "base64 ";
045
046    /**
047     *  Prevent instantiation.
048     */
049    private Serializer()
050    {}
051    
052    /**
053     * Deserializes a Base64-encoded String into a HashMap. Both the keys and values
054     * must implement {@link java.io.Serializable}.
055     * @param rawString the String contents containing the map to be deserialized
056     * @return the attributes, parsed into a Map
057     * @throws IOException if the contents cannot be parsed for any reason
058     */
059    @SuppressWarnings("unchecked")
060    public static Map< String, ? extends Serializable > deserializeFromBase64( String rawString ) throws IOException {
061        // Decode from Base64-encoded String to byte array
062        byte[] decodedBytes = Base64.getDecoder().decode( rawString.getBytes( StandardCharsets.UTF_8 ) );
063        
064        // Deserialize from the input stream to the Map
065        InputStream bytesIn = new ByteArrayInputStream( decodedBytes );
066        try( ObjectInputStream in = new ObjectInputStream( bytesIn ) ) {
067            return ( HashMap< String, Serializable > )in.readObject();
068        } catch ( ClassNotFoundException e ) {
069            throw new IOException( "Could not deserialiaze user profile attributes. Reason: " + e.getMessage() );
070        }
071    }
072
073    /**
074     * Serializes a Map and formats it into a Base64-encoded String. For ease of serialization, the Map contents
075     * are first copied into a HashMap, then serialized into a byte array that is encoded as a Base64 String.
076     * @param map the Map to serialize
077     * @return a String representing the serialized form of the Map
078     * @throws IOException If serialization cannot be done
079     */
080    public static String serializeToBase64( Map< String, Serializable > map ) throws IOException {
081        // Load the Map contents into a defensive HashMap
082        Map< String, Serializable > serialMap = new HashMap<>();
083        serialMap.putAll( map );
084        
085        // Serialize the Map to an output stream
086        ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
087        ObjectOutputStream out = new ObjectOutputStream( bytesOut );
088        out.writeObject( serialMap );
089        out.close();
090        
091        // Transform to Base64-encoded String
092        byte[] result = Base64.getEncoder().encode( bytesOut.toByteArray() );
093        return new String( result ) ;
094    }
095
096}