1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.chemistry.opencmis.server.impl.atompub;
20
21 import org.apache.chemistry.opencmis.commons.PropertyIds;
22 import org.apache.chemistry.opencmis.commons.data.AllowableActions;
23 import org.apache.chemistry.opencmis.commons.data.ContentStream;
24 import org.apache.chemistry.opencmis.commons.data.FailedToDeleteData;
25 import org.apache.chemistry.opencmis.commons.data.ObjectData;
26 import org.apache.chemistry.opencmis.commons.data.Properties;
27 import org.apache.chemistry.opencmis.commons.data.PropertyData;
28 import org.apache.chemistry.opencmis.commons.data.PropertyString;
29 import org.apache.chemistry.opencmis.commons.enums.IncludeRelationships;
30 import org.apache.chemistry.opencmis.commons.enums.UnfileObject;
31 import org.apache.chemistry.opencmis.commons.enums.VersioningState;
32 import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
33 import org.apache.chemistry.opencmis.commons.impl.Constants;
34 import org.apache.chemistry.opencmis.commons.impl.MimeHelper;
35 import org.apache.chemistry.opencmis.commons.impl.ReturnVersion;
36 import org.apache.chemistry.opencmis.commons.impl.UrlBuilder;
37 import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl;
38 import org.apache.chemistry.opencmis.commons.server.CallContext;
39 import org.apache.chemistry.opencmis.commons.server.CmisService;
40 import org.apache.chemistry.opencmis.commons.server.ObjectInfo;
41 import org.apache.chemistry.opencmis.commons.spi.Holder;
42 import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.RESOURCE_CONTENT;
43 import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.RESOURCE_ENTRY;
44 import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.compileBaseUrl;
45 import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.compileUrl;
46 import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.writeObjectEntry;
47 import static org.apache.chemistry.opencmis.server.shared.HttpUtils.getBooleanParameter;
48 import static org.apache.chemistry.opencmis.server.shared.HttpUtils.getEnumParameter;
49 import static org.apache.chemistry.opencmis.server.shared.HttpUtils.getStringParameter;
50
51 import java.io.BufferedInputStream;
52 import java.io.BufferedOutputStream;
53 import java.io.InputStream;
54 import java.io.OutputStream;
55 import java.io.PrintWriter;
56
57 import java.math.BigInteger;
58
59 import java.util.Map;
60
61 import javax.servlet.http.HttpServletRequest;
62 import javax.servlet.http.HttpServletResponse;
63
64
65
66
67
68 public final class ObjectService
69 {
70 private static final int BUFFER_SIZE = 64 * 1024;
71
72 private ObjectService( )
73 {
74 }
75
76
77
78
79 public static void create( CallContext context, CmisService service, String repositoryId,
80 HttpServletRequest request, HttpServletResponse response )
81 throws Exception
82 {
83
84 String folderId = getStringParameter( request, Constants.PARAM_ID );
85 String sourceFolderId = getStringParameter( request, Constants.PARAM_SOURCE_FOLDER_ID );
86 VersioningState versioningState = getEnumParameter( request, Constants.PARAM_VERSIONIG_STATE,
87 VersioningState.class );
88
89 AtomEntryParser parser = new AtomEntryParser( context.getTempDirectory( ), context.getMemoryThreshold( ) );
90 parser.setIgnoreAtomContentSrc( true );
91 parser.parse( request.getInputStream( ) );
92
93 String objectId = parser.getId( );
94
95
96 String newObjectId = null;
97
98 if ( objectId == null )
99 {
100
101 newObjectId = service.create( repositoryId, parser.getProperties( ), folderId,
102 parser.getContentStream( ), versioningState, parser.getPolicyIds( ), null );
103 }
104 else
105 {
106 if ( ( sourceFolderId == null ) || ( sourceFolderId.trim( ).length( ) == 0 ) )
107 {
108
109 service.addObjectToFolder( repositoryId, objectId, folderId, null, null );
110 newObjectId = objectId;
111 }
112 else
113 {
114
115 Holder<String> objectIdHolder = new Holder<String>( objectId );
116 service.moveObject( repositoryId, objectIdHolder, folderId, sourceFolderId, null );
117 newObjectId = objectIdHolder.getValue( );
118 }
119 }
120
121 ObjectInfo objectInfo = service.getObjectInfo( repositoryId, newObjectId );
122
123 if ( objectInfo == null )
124 {
125 throw new CmisRuntimeException( "Object Info is missing!" );
126 }
127
128 ObjectData object = objectInfo.getObject( );
129
130 if ( object == null )
131 {
132 throw new CmisRuntimeException( "Object is null!" );
133 }
134
135
136 UrlBuilder baseUrl = compileBaseUrl( request, repositoryId );
137
138 response.setStatus( HttpServletResponse.SC_CREATED );
139 response.setContentType( Constants.MEDIATYPE_ENTRY );
140 response.setHeader( "Location", compileUrl( baseUrl, RESOURCE_ENTRY, newObjectId ) );
141
142
143 AtomEntry entry = new AtomEntry( );
144 entry.startDocument( response.getOutputStream( ) );
145 writeObjectEntry( service, entry, object, null, repositoryId, null, null, baseUrl, true );
146 entry.endDocument( );
147 }
148
149
150
151
152 public static void createRelationship( CallContext context, CmisService service, String repositoryId,
153 HttpServletRequest request, HttpServletResponse response )
154 throws Exception
155 {
156
157 AtomEntryParser parser = new AtomEntryParser( request.getInputStream( ), context.getTempDirectory( ),
158 context.getMemoryThreshold( ) );
159
160
161 String newObjectId = service.createRelationship( repositoryId, parser.getProperties( ),
162 parser.getPolicyIds( ), null, null, null );
163
164 ObjectInfo objectInfo = service.getObjectInfo( repositoryId, newObjectId );
165
166 if ( objectInfo == null )
167 {
168 throw new CmisRuntimeException( "Object Info is missing!" );
169 }
170
171 ObjectData object = objectInfo.getObject( );
172
173 if ( object == null )
174 {
175 throw new CmisRuntimeException( "Object is null!" );
176 }
177
178
179 UrlBuilder baseUrl = compileBaseUrl( request, repositoryId );
180
181 response.setStatus( HttpServletResponse.SC_CREATED );
182 response.setContentType( Constants.MEDIATYPE_ENTRY );
183 response.setHeader( "Location", compileUrl( baseUrl, RESOURCE_ENTRY, newObjectId ) );
184
185
186 AtomEntry entry = new AtomEntry( );
187 entry.startDocument( response.getOutputStream( ) );
188 writeObjectEntry( service, entry, object, null, repositoryId, null, null, baseUrl, true );
189 entry.endDocument( );
190 }
191
192
193
194
195 public static void deleteObject( CallContext context, CmisService service, String repositoryId,
196 HttpServletRequest request, HttpServletResponse response )
197 {
198
199 String objectId = getStringParameter( request, Constants.PARAM_ID );
200 Boolean allVersions = getBooleanParameter( request, Constants.PARAM_ALL_VERSIONS );
201
202
203 service.deleteObjectOrCancelCheckOut( repositoryId, objectId, allVersions, null );
204
205
206 response.setStatus( HttpServletResponse.SC_NO_CONTENT );
207 }
208
209
210
211
212 public static void deleteContentStream( CallContext context, CmisService service, String repositoryId,
213 HttpServletRequest request, HttpServletResponse response )
214 {
215
216 String objectId = getStringParameter( request, Constants.PARAM_ID );
217 String changeToken = getStringParameter( request, Constants.PARAM_CHANGE_TOKEN );
218
219
220 service.deleteContentStream( repositoryId, new Holder<String>( objectId ),
221 ( changeToken == null ) ? null : new Holder<String>( changeToken ), null );
222
223
224 response.setStatus( HttpServletResponse.SC_NO_CONTENT );
225 }
226
227
228
229
230 public static void setContentStream( CallContext context, CmisService service, String repositoryId,
231 HttpServletRequest request, HttpServletResponse response )
232 throws Exception
233 {
234
235 String objectId = getStringParameter( request, Constants.PARAM_ID );
236 String changeToken = getStringParameter( request, Constants.PARAM_CHANGE_TOKEN );
237 Boolean overwriteFlag = getBooleanParameter( request, Constants.PARAM_OVERWRITE_FLAG );
238
239 ContentStreamImpl contentStream = new ContentStreamImpl( );
240 contentStream.setStream( request.getInputStream( ) );
241 contentStream.setMimeType( request.getHeader( "Content-Type" ) );
242
243 String lengthStr = request.getHeader( "Content-Length" );
244
245 if ( lengthStr != null )
246 {
247 try
248 {
249 contentStream.setLength( new BigInteger( lengthStr ) );
250 }
251 catch ( NumberFormatException e )
252 {
253 }
254 }
255
256 String contentDisposition = request.getHeader( MimeHelper.CONTENT_DISPOSITION );
257
258 if ( contentDisposition != null )
259 {
260 contentStream.setFileName( MimeHelper.decodeContentDispositionFilename( contentDisposition ) );
261 }
262
263
264 Holder<String> objectIdHolder = new Holder<String>( objectId );
265 service.setContentStream( repositoryId, objectIdHolder, overwriteFlag,
266 ( changeToken == null ) ? null : new Holder<String>( changeToken ), contentStream, null );
267
268
269 String newObjectId = ( ( objectIdHolder.getValue( ) == null ) ? objectId : objectIdHolder.getValue( ) );
270 String location = compileUrl( compileBaseUrl( request, repositoryId ), RESOURCE_CONTENT, newObjectId );
271
272 response.setStatus( HttpServletResponse.SC_CREATED );
273 response.setHeader( "Content-Location", location );
274 response.setHeader( "Location", location );
275 }
276
277
278
279
280 public static void deleteTree( CallContext context, CmisService service, String repositoryId,
281 HttpServletRequest request, HttpServletResponse response )
282 throws Exception
283 {
284
285 String folderId = getStringParameter( request, Constants.PARAM_ID );
286 Boolean allVersions = getBooleanParameter( request, Constants.PARAM_ALL_VERSIONS );
287 UnfileObject unfileObjects = getEnumParameter( request, Constants.PARAM_UNFILE_OBJECTS, UnfileObject.class );
288 Boolean continueOnFailure = getBooleanParameter( request, Constants.PARAM_CONTINUE_ON_FAILURE );
289
290
291 FailedToDeleteData ftd = service.deleteTree( repositoryId, folderId, allVersions, unfileObjects,
292 continueOnFailure, null );
293
294 if ( ( ftd != null ) && ( ftd.getIds( ) != null ) && ( ftd.getIds( ).size( ) > 0 ) )
295 {
296
297 response.setStatus( HttpServletResponse.SC_INTERNAL_SERVER_ERROR );
298 response.setContentType( "text/plain" );
299
300 PrintWriter pw = response.getWriter( );
301
302 pw.println( "Failed to delete the following objects:" );
303
304 for ( String id : ftd.getIds( ) )
305 {
306 pw.println( id );
307 }
308
309 pw.flush( );
310
311 return;
312 }
313
314
315 response.setStatus( HttpServletResponse.SC_NO_CONTENT );
316 }
317
318
319
320
321 public static void getObject( CallContext context, CmisService service, String repositoryId,
322 HttpServletRequest request, HttpServletResponse response )
323 throws Exception
324 {
325
326 String objectId = getStringParameter( request, Constants.PARAM_ID );
327 ReturnVersion returnVersion = getEnumParameter( request, Constants.PARAM_RETURN_VERSION, ReturnVersion.class );
328 String filter = getStringParameter( request, Constants.PARAM_FILTER );
329 Boolean includeAllowableActions = getBooleanParameter( request, Constants.PARAM_ALLOWABLE_ACTIONS );
330 IncludeRelationships includeRelationships = getEnumParameter( request, Constants.PARAM_RELATIONSHIPS,
331 IncludeRelationships.class );
332 String renditionFilter = getStringParameter( request, Constants.PARAM_RENDITION_FILTER );
333 Boolean includePolicyIds = getBooleanParameter( request, Constants.PARAM_POLICY_IDS );
334 Boolean includeAcl = getBooleanParameter( request, Constants.PARAM_ACL );
335
336
337 ObjectData object = null;
338
339 if ( ( returnVersion == ReturnVersion.LATEST ) || ( returnVersion == ReturnVersion.LASTESTMAJOR ) )
340 {
341 object = service.getObjectOfLatestVersion( repositoryId, objectId, null,
342 returnVersion == ReturnVersion.LASTESTMAJOR, filter, includeAllowableActions, includeRelationships,
343 renditionFilter, includePolicyIds, includeAcl, null );
344 }
345 else
346 {
347 object = service.getObject( repositoryId, objectId, filter, includeAllowableActions, includeRelationships,
348 renditionFilter, includePolicyIds, includeAcl, null );
349 }
350
351 if ( object == null )
352 {
353 throw new CmisRuntimeException( "Object is null!" );
354 }
355
356 ObjectInfo objectInfo = service.getObjectInfo( repositoryId, objectId );
357
358 if ( objectInfo == null )
359 {
360 throw new CmisRuntimeException( "Object Info is missing!" );
361 }
362
363
364 response.setStatus( HttpServletResponse.SC_OK );
365 response.setContentType( Constants.MEDIATYPE_ENTRY );
366
367
368 UrlBuilder baseUrl = compileBaseUrl( request, repositoryId );
369
370 AtomEntry entry = new AtomEntry( );
371 entry.startDocument( response.getOutputStream( ) );
372 writeObjectEntry( service, entry, object, null, repositoryId, null, null, baseUrl, true );
373 entry.endDocument( );
374 }
375
376
377
378
379 public static void getObjectByPath( CallContext context, CmisService service, String repositoryId,
380 HttpServletRequest request, HttpServletResponse response )
381 throws Exception
382 {
383
384 String path = getStringParameter( request, Constants.PARAM_PATH );
385 String filter = getStringParameter( request, Constants.PARAM_FILTER );
386 Boolean includeAllowableActions = getBooleanParameter( request, Constants.PARAM_ALLOWABLE_ACTIONS );
387 IncludeRelationships includeRelationships = getEnumParameter( request, Constants.PARAM_RELATIONSHIPS,
388 IncludeRelationships.class );
389 String renditionFilter = getStringParameter( request, Constants.PARAM_RENDITION_FILTER );
390 Boolean includePolicyIds = getBooleanParameter( request, Constants.PARAM_POLICY_IDS );
391 Boolean includeAcl = getBooleanParameter( request, Constants.PARAM_ACL );
392
393
394 ObjectData object = service.getObjectByPath( repositoryId, path, filter, includeAllowableActions,
395 includeRelationships, renditionFilter, includePolicyIds, includeAcl, null );
396
397 if ( object == null )
398 {
399 throw new CmisRuntimeException( "Object is null!" );
400 }
401
402 ObjectInfo objectInfo = service.getObjectInfo( repositoryId, object.getId( ) );
403
404 if ( objectInfo == null )
405 {
406 throw new CmisRuntimeException( "Object Info is missing!" );
407 }
408
409
410 response.setStatus( HttpServletResponse.SC_OK );
411 response.setContentType( Constants.MEDIATYPE_ENTRY );
412
413
414 UrlBuilder baseUrl = compileBaseUrl( request, repositoryId );
415
416 AtomEntry entry = new AtomEntry( );
417 entry.startDocument( response.getOutputStream( ) );
418 writeObjectEntry( service, entry, object, null, repositoryId, null, null, baseUrl, true );
419 entry.endDocument( );
420 }
421
422
423
424
425 public static void getAllowableActions( CallContext context, CmisService service, String repositoryId,
426 HttpServletRequest request, HttpServletResponse response )
427 throws Exception
428 {
429
430 String objectId = getStringParameter( request, Constants.PARAM_ID );
431
432
433 AllowableActions allowableActions = service.getAllowableActions( repositoryId, objectId, null );
434
435 if ( allowableActions == null )
436 {
437 throw new CmisRuntimeException( "Allowable Actions is null!" );
438 }
439
440
441 response.setStatus( HttpServletResponse.SC_OK );
442 response.setContentType( Constants.MEDIATYPE_ALLOWABLEACTION );
443
444
445 AllowableActionsDocument allowableActionsDocument = new AllowableActionsDocument( );
446 allowableActionsDocument.writeAllowableActions( allowableActions, response.getOutputStream( ) );
447 }
448
449
450
451
452 public static void getContentStream( CallContext context, CmisService service, String repositoryId,
453 HttpServletRequest request, HttpServletResponse response )
454 throws Exception
455 {
456
457 String objectId = getStringParameter( request, Constants.PARAM_ID );
458 String streamId = getStringParameter( request, Constants.PARAM_STREAM_ID );
459
460 BigInteger offset = context.getOffset( );
461 BigInteger length = context.getLength( );
462
463
464 ContentStream content = service.getContentStream( repositoryId, objectId, streamId, offset, length, null );
465
466 if ( ( content == null ) || ( content.getStream( ) == null ) )
467 {
468 throw new CmisRuntimeException( "Content stream is null!" );
469 }
470
471 String contentType = content.getMimeType( );
472
473 if ( contentType == null )
474 {
475 contentType = Constants.MEDIATYPE_OCTETSTREAM;
476 }
477
478
479 if ( ( offset == null ) && ( length == null ) )
480 {
481 response.setStatus( HttpServletResponse.SC_OK );
482 }
483 else
484 {
485 response.setStatus( HttpServletResponse.SC_PARTIAL_CONTENT );
486 }
487
488 response.setContentType( contentType );
489
490
491 InputStream in = new BufferedInputStream( content.getStream( ), BUFFER_SIZE );
492 OutputStream out = new BufferedOutputStream( response.getOutputStream( ) );
493
494 byte[] buffer = new byte[BUFFER_SIZE];
495 int b;
496
497 while ( ( b = in.read( buffer ) ) > -1 )
498 {
499 out.write( buffer, 0, b );
500 }
501
502 in.close( );
503 out.flush( );
504 }
505
506
507
508
509 public static void updateProperties( CallContext context, CmisService service, String repositoryId,
510 HttpServletRequest request, HttpServletResponse response )
511 throws Exception
512 {
513
514 String objectId = getStringParameter( request, Constants.PARAM_ID );
515 Boolean checkin = getBooleanParameter( request, Constants.PARAM_CHECK_IN );
516 String checkinComment = getStringParameter( request, Constants.PARAM_CHECKIN_COMMENT );
517 Boolean major = getBooleanParameter( request, Constants.PARAM_MAJOR );
518
519 AtomEntryParser parser = new AtomEntryParser( request.getInputStream( ), context.getTempDirectory( ),
520 context.getMemoryThreshold( ) );
521
522
523 Holder<String> objectIdHolder = new Holder<String>( objectId );
524
525 if ( ( checkin != null ) && ( checkin.booleanValue( ) ) )
526 {
527 service.checkIn( repositoryId, objectIdHolder, major, parser.getProperties( ),
528 parser.getContentStream( ), checkinComment, parser.getPolicyIds( ), null, null, null );
529 }
530 else
531 {
532 String changeToken = extractChangeToken( parser.getProperties( ) );
533
534 service.updateProperties( repositoryId, objectIdHolder,
535 ( changeToken == null ) ? null : new Holder<String>( changeToken ), parser.getProperties( ), null );
536 }
537
538 ObjectInfo objectInfo = service.getObjectInfo( repositoryId, objectIdHolder.getValue( ) );
539
540 if ( objectInfo == null )
541 {
542 throw new CmisRuntimeException( "Object Info is missing!" );
543 }
544
545 ObjectData object = objectInfo.getObject( );
546
547 if ( object == null )
548 {
549 throw new CmisRuntimeException( "Object is null!" );
550 }
551
552
553 UrlBuilder baseUrl = compileBaseUrl( request, repositoryId );
554 String location = compileUrl( baseUrl, RESOURCE_ENTRY, objectIdHolder.getValue( ) );
555
556 response.setStatus( HttpServletResponse.SC_CREATED );
557 response.setContentType( Constants.MEDIATYPE_ENTRY );
558 response.setHeader( "Content-Location", location );
559 response.setHeader( "Location", location );
560
561
562 AtomEntry entry = new AtomEntry( );
563 entry.startDocument( response.getOutputStream( ) );
564 writeObjectEntry( service, entry, object, null, repositoryId, null, null, baseUrl, true );
565 entry.endDocument( );
566 }
567
568
569
570
571 private static String extractChangeToken( Properties properties )
572 {
573 if ( properties == null )
574 {
575 return null;
576 }
577
578 Map<String, PropertyData<?>> propertiesMap = properties.getProperties( );
579
580 if ( propertiesMap == null )
581 {
582 return null;
583 }
584
585 PropertyData<?> changeLogProperty = propertiesMap.get( PropertyIds.CHANGE_TOKEN );
586
587 if ( !( changeLogProperty instanceof PropertyString ) )
588 {
589 return null;
590 }
591
592 return ( (PropertyString) changeLogProperty ).getFirstValue( );
593 }
594 }