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