001/* 002 * Copyright 2011-2013 UnboundID Corp. 003 * 004 * This program is free software; you can redistribute it and/or modify 005 * it under the terms of the GNU General Public License (GPLv2 only) 006 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 007 * as published by the Free Software Foundation. 008 * 009 * This program is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 012 * GNU General Public License for more details. 013 * 014 * You should have received a copy of the GNU General Public License 015 * along with this program; if not, see <http://www.gnu.org/licenses>. 016 */ 017 018package com.unboundid.scim.marshal.json; 019 020import com.unboundid.scim.data.BaseResource; 021import com.unboundid.scim.data.BulkConfig; 022import com.unboundid.scim.data.ResourceFactory; 023import com.unboundid.scim.marshal.Unmarshaller; 024import com.unboundid.scim.schema.ResourceDescriptor; 025import com.unboundid.scim.sdk.BulkContentHandler; 026import com.unboundid.scim.sdk.Debug; 027import com.unboundid.scim.sdk.InvalidResourceException; 028import com.unboundid.scim.sdk.Resources; 029import com.unboundid.scim.sdk.SCIMException; 030import com.unboundid.scim.sdk.ServerErrorException; 031import org.json.JSONArray; 032import org.json.JSONException; 033import org.json.JSONObject; 034import org.json.JSONTokener; 035 036import java.io.BufferedInputStream; 037import java.io.File; 038import java.io.FileInputStream; 039import java.io.IOException; 040import java.io.InputStream; 041import java.util.ArrayList; 042import java.util.Collections; 043import java.util.List; 044import java.util.concurrent.atomic.AtomicInteger; 045 046import static com.unboundid.scim.marshal.json.JsonParser.makeCaseInsensitive; 047 048 049 050/** 051 * This class provides a SCIM object un-marshaller implementation to read SCIM 052 * objects from their JSON representation. 053 */ 054public class JsonUnmarshaller implements Unmarshaller 055{ 056 /** 057 * {@inheritDoc} 058 */ 059 public <R extends BaseResource> R unmarshal( 060 final InputStream inputStream, 061 final ResourceDescriptor resourceDescriptor, 062 final ResourceFactory<R> resourceFactory) throws InvalidResourceException 063 { 064 try 065 { 066 final JSONObject jsonObject = 067 makeCaseInsensitive(new JSONObject(new JSONTokener(inputStream))); 068 069 final JsonParser parser = new JsonParser(); 070 return parser.unmarshal(jsonObject, resourceDescriptor, resourceFactory, 071 null); 072 } 073 catch(JSONException e) 074 { 075 throw new InvalidResourceException("Error while reading JSON: " + 076 e.getMessage(), e); 077 } 078 } 079 080 /** 081 * {@inheritDoc} 082 */ 083 public <R extends BaseResource> Resources<R> unmarshalResources( 084 final InputStream inputStream, 085 final ResourceDescriptor resourceDescriptor, 086 final ResourceFactory<R> resourceFactory) throws InvalidResourceException 087 { 088 try 089 { 090 final JsonParser parser = new JsonParser(); 091 final JSONObject jsonObject = 092 makeCaseInsensitive(new JSONObject(new JSONTokener(inputStream))); 093 094 int totalResults = 0; 095 if(jsonObject.has("totalresults")) 096 { 097 totalResults = jsonObject.getInt("totalresults"); 098 } 099 100 int startIndex = 1; 101 if(jsonObject.has("startindex")) 102 { 103 startIndex = jsonObject.getInt("startindex"); 104 } 105 106 final JSONArray schemas = jsonObject.optJSONArray("schemas"); 107 108 List<R> resources = Collections.emptyList(); 109 if(jsonObject.has("resources")) 110 { 111 JSONArray resourcesArray = jsonObject.getJSONArray("resources"); 112 resources = new ArrayList<R>(resourcesArray.length()); 113 for(int i = 0; i < resourcesArray.length(); i++) 114 { 115 JSONObject subObject = makeCaseInsensitive( 116 resourcesArray.getJSONObject(i)); 117 118 R resource = parser.unmarshal(subObject, 119 resourceDescriptor, 120 resourceFactory, 121 schemas); 122 resources.add(resource); 123 } 124 } 125 126 return new Resources<R>(resources, totalResults, startIndex); 127 } 128 catch(JSONException e) 129 { 130 throw new InvalidResourceException("Error while reading JSON: " + 131 e.getMessage(), e); 132 } 133 } 134 135 /** 136 * {@inheritDoc} 137 */ 138 public SCIMException unmarshalError(final InputStream inputStream) 139 throws InvalidResourceException 140 { 141 try 142 { 143 final JSONObject jsonObject = 144 makeCaseInsensitive(new JSONObject(new JSONTokener(inputStream))); 145 146 if(jsonObject.has("errors")) 147 { 148 JSONArray errors = jsonObject.getJSONArray("errors"); 149 if(errors.length() >= 1) 150 { 151 JSONObject error = errors.getJSONObject(0); 152 int code = error.optInt("code"); 153 String description = error.optString("description"); 154 return SCIMException.createException(code, description); 155 } 156 } 157 return null; 158 } 159 catch (JSONException e) 160 { 161 throw new InvalidResourceException("Error while reading JSON: " + 162 e.getMessage(), e); 163 } 164 } 165 166 167 168 /** 169 * {@inheritDoc} 170 */ 171 public void bulkUnmarshal(final InputStream inputStream, 172 final BulkConfig bulkConfig, 173 final BulkContentHandler handler) 174 throws SCIMException 175 { 176 final JsonBulkParser bulkUnmarshaller = 177 new JsonBulkParser(inputStream, bulkConfig, handler); 178 bulkUnmarshaller.unmarshal(); 179 } 180 181 182 183 /** 184 * {@inheritDoc} 185 */ 186 public void bulkUnmarshal(final File file, 187 final BulkConfig bulkConfig, 188 final BulkContentHandler handler) 189 throws SCIMException 190 { 191 // First pass: ensure the number of operations is less than the max, 192 // and save the failOnErrrors value. 193 final AtomicInteger failOnErrorsValue = new AtomicInteger(-1); 194 final BulkContentHandler preProcessHandler = new BulkContentHandler() 195 { 196 @Override 197 public void handleFailOnErrors(final int failOnErrors) 198 { 199 failOnErrorsValue.set(failOnErrors); 200 } 201 }; 202 try 203 { 204 final FileInputStream fileInputStream = new FileInputStream(file); 205 try 206 { 207 final BufferedInputStream bufferedInputStream = 208 new BufferedInputStream(fileInputStream); 209 try 210 { 211 final JsonBulkParser jsonBulkParser = 212 new JsonBulkParser(bufferedInputStream, bulkConfig, 213 preProcessHandler); 214 jsonBulkParser.setSkipOperations(true); 215 jsonBulkParser.unmarshal(); 216 } 217 finally 218 { 219 bufferedInputStream.close(); 220 } 221 } 222 finally 223 { 224 fileInputStream.close(); 225 } 226 } 227 catch (IOException e) 228 { 229 Debug.debugException(e); 230 throw new ServerErrorException( 231 "Error pre-processing bulk request: " + e.getMessage()); 232 } 233 234 int failOnErrors = failOnErrorsValue.get(); 235 if (failOnErrors != -1) 236 { 237 handler.handleFailOnErrors(failOnErrors); 238 } 239 240 // Second pass: Parse fully. 241 try 242 { 243 final FileInputStream fileInputStream = new FileInputStream(file); 244 try 245 { 246 final BufferedInputStream bufferedInputStream = 247 new BufferedInputStream(fileInputStream); 248 try 249 { 250 final JsonBulkParser jsonBulkParser = 251 new JsonBulkParser(bufferedInputStream, bulkConfig, handler); 252 jsonBulkParser.unmarshal(); 253 } 254 finally 255 { 256 bufferedInputStream.close(); 257 } 258 } 259 finally 260 { 261 fileInputStream.close(); 262 } 263 } 264 catch (IOException e) 265 { 266 Debug.debugException(e); 267 throw new ServerErrorException( 268 "Error parsing bulk request: " + e.getMessage()); 269 } 270 } 271}