001/* 002 * acme4j - Java ACME client 003 * 004 * Copyright (C) 2015 Richard "Shred" Körber 005 * http://acme4j.shredzone.org 006 * 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 013 */ 014package org.shredzone.acme4j.toolbox; 015 016import static org.shredzone.acme4j.toolbox.AcmeUtils.base64UrlEncode; 017 018import java.security.Key; 019import java.security.PublicKey; 020import java.time.Duration; 021import java.time.Instant; 022import java.time.format.DateTimeFormatter; 023import java.util.Collection; 024import java.util.Collections; 025import java.util.LinkedHashMap; 026import java.util.Map; 027import java.util.Objects; 028 029import edu.umd.cs.findbugs.annotations.Nullable; 030import org.jose4j.json.JsonUtil; 031 032/** 033 * Builder for JSON structures. 034 * <p> 035 * Example: 036 * <pre> 037 * JSONBuilder cb = new JSONBuilder(); 038 * cb.put("foo", 123).put("bar", "hello world"); 039 * cb.object("sub").put("data", "subdata"); 040 * cb.array("array", 123, 456, 789); 041 * </pre> 042 */ 043public class JSONBuilder { 044 045 private final Map<String, Object> data = new LinkedHashMap<>(); 046 047 /** 048 * Puts a property. If a property with the key exists, it will be replaced. 049 * 050 * @param key 051 * Property key 052 * @param value 053 * Property value 054 * @return {@code this} 055 */ 056 public JSONBuilder put(String key, @Nullable Object value) { 057 data.put(Objects.requireNonNull(key, "key"), value); 058 return this; 059 } 060 061 /** 062 * Puts an {@link Instant} to the JSON. If a property with the key exists, it will be 063 * replaced. 064 * 065 * @param key 066 * Property key 067 * @param value 068 * Property {@link Instant} value 069 * @return {@code this} 070 */ 071 public JSONBuilder put(String key, @Nullable Instant value) { 072 if (value == null) { 073 put(key, (Object) null); 074 return this; 075 } 076 077 put(key, DateTimeFormatter.ISO_INSTANT.format(value)); 078 return this; 079 } 080 081 /** 082 * Puts a {@link Duration} to the JSON. If a property with the key exists, it will be 083 * replaced. 084 * 085 * @param key 086 * Property key 087 * @param value 088 * Property {@link Duration} value 089 * @return {@code this} 090 * @since 2.3 091 */ 092 public JSONBuilder put(String key, @Nullable Duration value) { 093 if (value == null) { 094 put(key, (Object) null); 095 return this; 096 } 097 098 put(key, value.getSeconds()); 099 return this; 100 } 101 102 /** 103 * Puts binary data to the JSON. The data is base64 url encoded. 104 * 105 * @param key 106 * Property key 107 * @param data 108 * Property data 109 * @return {@code this} 110 */ 111 public JSONBuilder putBase64(String key, byte[] data) { 112 return put(key, base64UrlEncode(data)); 113 } 114 115 /** 116 * Puts a {@link Key} into the claim. The key is serializied as JWK. 117 * 118 * @param key 119 * Property key 120 * @param publickey 121 * {@link PublicKey} to serialize 122 * @return {@code this} 123 */ 124 public JSONBuilder putKey(String key, PublicKey publickey) { 125 Objects.requireNonNull(publickey, "publickey"); 126 127 data.put(key, JoseUtils.publicKeyToJWK(publickey)); 128 return this; 129 } 130 131 /** 132 * Creates an object for the given key. 133 * 134 * @param key 135 * Key of the object 136 * @return Newly created {@link JSONBuilder} for the object. 137 */ 138 public JSONBuilder object(String key) { 139 JSONBuilder subBuilder = new JSONBuilder(); 140 data.put(key, subBuilder.data); 141 return subBuilder; 142 } 143 144 /** 145 * Puts an array. 146 * 147 * @param key 148 * Property key 149 * @param values 150 * Collection of property values 151 * @return {@code this} 152 */ 153 public JSONBuilder array(String key, Collection<?> values) { 154 data.put(key, values); 155 return this; 156 } 157 158 /** 159 * Returns a {@link Map} representation of the current state. 160 * 161 * @return {@link Map} of the current state 162 */ 163 public Map<String, Object> toMap() { 164 return Collections.unmodifiableMap(data); 165 } 166 167 /** 168 * Returns a {@link JSON} representation of the current state. 169 * 170 * @return {@link JSON} of the current state 171 */ 172 public JSON toJSON() { 173 return JSON.parse(toString()); 174 } 175 176 /** 177 * Returns a JSON string representation of the current state. 178 */ 179 @Override 180 public String toString() { 181 return JsonUtil.toJson(data); 182 } 183 184}