001 /*
002 * Copyright 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.sdk;
019
020 import com.unboundid.scim.marshal.Marshaller;
021 import com.unboundid.scim.marshal.StreamMarshaller;
022 import com.unboundid.scim.marshal.json.JsonStreamMarshaller;
023 import com.unboundid.scim.marshal.xml.XmlStreamMarshaller;
024 import com.unboundid.scim.wink.RequestContext;
025 import com.unboundid.scim.wink.SCIMApplication;
026
027 import javax.ws.rs.core.MediaType;
028 import java.io.BufferedOutputStream;
029 import java.io.File;
030 import java.io.FileInputStream;
031 import java.io.FileOutputStream;
032 import java.io.OutputStream;
033 import java.util.Collections;
034 import java.util.Set;
035 import java.util.logging.Level;
036
037
038
039 /**
040 * Implements a SCIMResponse to handle bulk responses without keeping the
041 * entire response in memory.
042 */
043 public class BulkStreamResponse implements SCIMResponse
044 {
045 private final File file;
046 private final StreamMarshaller streamMarshaller;
047
048
049 /**
050 * Create a new bulk stream response.
051 *
052 * @param application The SCIM JAX-RS application.
053 * @param requestContext The bulk request context.
054 *
055 * @throws SCIMException If the bulk stream response could not be created.
056 */
057 public BulkStreamResponse(final SCIMApplication application,
058 final RequestContext requestContext)
059 throws SCIMException
060 {
061 try
062 {
063 file = File.createTempFile(
064 "scim-bulk-response-",
065 "." + requestContext.getProduceMediaType().getSubtype(),
066 application.getTmpDataDir());
067 file.deleteOnExit();
068 }
069 catch (Exception e)
070 {
071 Debug.debugException(e);
072 throw new ServerErrorException(
073 "Cannot create a temporary file for the bulk response" +
074 e.getMessage());
075 }
076
077 final OutputStream outputStream;
078 try
079 {
080 outputStream = new BufferedOutputStream(new FileOutputStream(file));
081 }
082 catch (Exception e)
083 {
084 Debug.debugException(e);
085 if (!file.delete())
086 {
087 Debug.debug(Level.WARNING, DebugType.OTHER,
088 "Could not delete temporary file " +
089 file.getAbsolutePath());
090 }
091
092 throw new ServerErrorException(
093 "Cannot create output stream for temporary file '" + file + "': " +
094 e.getMessage());
095 }
096
097 try
098 {
099 if (requestContext.getProduceMediaType().equals(
100 MediaType.APPLICATION_JSON_TYPE))
101 {
102 streamMarshaller = new JsonStreamMarshaller(outputStream);
103 }
104 else
105 {
106 streamMarshaller = new XmlStreamMarshaller(outputStream);
107 }
108
109 // Bulk responses contain no data so there is only core schema.
110 final Set<String> schemaURIs =
111 Collections.singleton(SCIMConstants.SCHEMA_URI_CORE);
112 streamMarshaller.writeBulkStart(-1, schemaURIs);
113 }
114 catch (SCIMException e)
115 {
116 Debug.debugException(e);
117
118 try
119 {
120 outputStream.close();
121 }
122 catch (Exception e1)
123 {
124 Debug.debugException(e1);
125 }
126 finally
127 {
128 if (!file.delete())
129 {
130 Debug.debug(Level.WARNING, DebugType.OTHER,
131 "Could not delete temporary file " +
132 file.getAbsolutePath());
133 }
134 }
135
136 throw e;
137 }
138 }
139
140
141
142 /**
143 * Write a bulk operation to the response.
144 *
145 * @param o The bulk operation to write.
146 *
147 * @throws SCIMException If the bulk operation could not be written.
148 */
149 public void writeBulkOperation(final BulkOperation o)
150 throws SCIMException
151 {
152 streamMarshaller.writeBulkOperation(o);
153 }
154
155
156
157 /**
158 * Release resources when this bulk stream response is no longer needed.
159 */
160 public void finalizeResponse()
161 {
162 if (file.exists() && !file.delete())
163 {
164 Debug.debug(Level.WARNING, DebugType.OTHER,
165 "Could not delete temporary file " +
166 file.getAbsolutePath());
167 }
168 }
169
170
171
172 /**
173 * {@inheritDoc}
174 */
175 @Override
176 public void marshal(final Marshaller marshaller,
177 final OutputStream outputStream)
178 throws Exception
179 {
180 try
181 {
182 // Finish writing the response to the temporary file.
183 streamMarshaller.writeBulkFinish();
184 streamMarshaller.close();
185
186 // Copy the temporary file to the output stream.
187 final FileInputStream inputStream = new FileInputStream(file);
188 final byte[] buffer = new byte[8192];
189 int bytesRead;
190 while ((bytesRead = inputStream.read(buffer)) != -1)
191 {
192 outputStream.write(buffer, 0, bytesRead);
193 }
194 }
195 finally
196 {
197 // Delete the temporary response file.
198 finalizeResponse();
199 }
200 }
201 }