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.Instant; 021import java.time.format.DateTimeFormatter; 022import java.util.Collections; 023import java.util.Map; 024import java.util.Objects; 025import java.util.TreeMap; 026 027import org.jose4j.json.JsonUtil; 028import org.jose4j.jwk.JsonWebKey; 029import org.jose4j.jwk.PublicJsonWebKey; 030import org.jose4j.lang.JoseException; 031import org.shredzone.acme4j.connector.Resource; 032import org.shredzone.acme4j.exception.AcmeProtocolException; 033 034/** 035 * Builder for JSON structures. 036 * <p> 037 * Example: 038 * <pre> 039 * JSONBuilder cb = new JSONBuilder(); 040 * cb.put("foo", 123).put("bar", "hello world"); 041 * cb.object("sub").put("data", "subdata"); 042 * cb.array("array", 123, 456, 789); 043 * </pre> 044 */ 045public class JSONBuilder { 046 047 private final Map<String, Object> data = new TreeMap<>(); 048 049 /** 050 * Puts a property. If a property with the key exists, it will be replaced. 051 * 052 * @param key 053 * Property key 054 * @param value 055 * Property value 056 * @return {@code this} 057 */ 058 public JSONBuilder put(String key, Object value) { 059 data.put(Objects.requireNonNull(key, "key"), value); 060 return this; 061 } 062 063 /** 064 * Puts an {@link Instant} to the JSON. If a property with the key exists, it will be 065 * replaced. 066 * 067 * @param key 068 * Property key 069 * @param value 070 * Property {@link Instant} value 071 * @return {@code this} 072 */ 073 public JSONBuilder put(String key, Instant value) { 074 if (value == null) { 075 put(key, (Object) null); 076 return this; 077 } 078 079 put(key, DateTimeFormatter.ISO_INSTANT.format(value)); 080 return this; 081 } 082 083 /** 084 * Puts a resource. 085 * 086 * @param resource 087 * Resource name 088 * @return {@code this} 089 */ 090 public JSONBuilder putResource(String resource) { 091 return put("resource", resource); 092 } 093 094 /** 095 * Puts a resource. 096 * 097 * @param resource 098 * {@link Resource} 099 * @return {@code this} 100 */ 101 public JSONBuilder putResource(Resource resource) { 102 return putResource(resource.path()); 103 } 104 105 /** 106 * Puts binary data to the JSON. The data is base64 url encoded. 107 * 108 * @param key 109 * Property key 110 * @param data 111 * Property data 112 * @return {@code this} 113 */ 114 public JSONBuilder putBase64(String key, byte[] data) { 115 return put(key, base64UrlEncode(data)); 116 } 117 118 /** 119 * Puts a {@link Key} into the claim. The key is serializied as JWK. 120 * 121 * @param key 122 * Property key 123 * @param publickey 124 * {@link PublicKey} to serialize 125 * @return {@code this} 126 */ 127 public JSONBuilder putKey(String key, PublicKey publickey) { 128 Objects.requireNonNull(publickey, "publickey"); 129 130 try { 131 final PublicJsonWebKey jwk = PublicJsonWebKey.Factory.newPublicJwk(publickey); 132 Map<String, Object> jwkParams = jwk.toParams(JsonWebKey.OutputControlLevel.PUBLIC_ONLY); 133 object(key).data.putAll(jwkParams); 134 return this; 135 } catch (JoseException ex) { 136 throw new AcmeProtocolException("Invalid key", ex); 137 } 138 } 139 140 /** 141 * Creates an object for the given key. 142 * 143 * @param key 144 * Key of the object 145 * @return Newly created {@link JSONBuilder} for the object. 146 */ 147 public JSONBuilder object(String key) { 148 JSONBuilder subBuilder = new JSONBuilder(); 149 data.put(key, subBuilder.data); 150 return subBuilder; 151 } 152 153 /** 154 * Puts an array. 155 * 156 * @param key 157 * Property key 158 * @param values 159 * Array of property values 160 * @return {@code this} 161 */ 162 public JSONBuilder array(String key, Object... values) { 163 data.put(key, values); 164 return this; 165 } 166 167 /** 168 * Returns a {@link Map} representation of the current state. 169 * 170 * @return {@link Map} of the current state 171 */ 172 public Map<String, Object> toMap() { 173 return Collections.unmodifiableMap(data); 174 } 175 176 /** 177 * Returns a {@link JSON} representation of the current state. 178 * 179 * @return {@link JSON} of the current state 180 */ 181 public JSON toJSON() { 182 return JSON.parse(toString()); 183 } 184 185 /** 186 * Returns a JSON string representation of the current state. 187 */ 188 @Override 189 public String toString() { 190 return JsonUtil.toJson(data); 191 } 192 193}