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.browser;
20
21 import org.apache.chemistry.opencmis.commons.PropertyIds;
22 import org.apache.chemistry.opencmis.commons.data.Ace;
23 import org.apache.chemistry.opencmis.commons.data.Acl;
24 import org.apache.chemistry.opencmis.commons.data.ContentStream;
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.definitions.PropertyDefinition;
29 import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
30 import org.apache.chemistry.opencmis.commons.enums.IncludeRelationships;
31 import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException;
32 import org.apache.chemistry.opencmis.commons.impl.Base64;
33 import org.apache.chemistry.opencmis.commons.impl.Constants;
34 import org.apache.chemistry.opencmis.commons.impl.TypeCache;
35 import org.apache.chemistry.opencmis.commons.impl.UrlBuilder;
36 import org.apache.chemistry.opencmis.commons.impl.dataobjects.AccessControlEntryImpl;
37 import org.apache.chemistry.opencmis.commons.impl.dataobjects.AccessControlListImpl;
38 import org.apache.chemistry.opencmis.commons.impl.dataobjects.AccessControlPrincipalDataImpl;
39 import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl;
40 import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertiesImpl;
41 import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyBooleanImpl;
42 import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyDateTimeImpl;
43 import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyDecimalImpl;
44 import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyHtmlImpl;
45 import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyIdImpl;
46 import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyIntegerImpl;
47 import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyStringImpl;
48 import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyUriImpl;
49 import org.apache.chemistry.opencmis.commons.impl.json.JSONObject;
50 import org.apache.chemistry.opencmis.commons.impl.json.JSONStreamAware;
51 import org.apache.chemistry.opencmis.commons.server.CallContext;
52 import org.apache.chemistry.opencmis.commons.server.CmisService;
53 import org.apache.chemistry.opencmis.server.impl.CallContextImpl;
54 import org.apache.chemistry.opencmis.server.shared.HttpUtils;
55 import static org.apache.chemistry.opencmis.server.shared.HttpUtils.getBooleanParameter;
56 import static org.apache.chemistry.opencmis.server.shared.HttpUtils.getStringParameter;
57
58 import java.io.IOException;
59
60 import java.math.BigDecimal;
61 import java.math.BigInteger;
62
63 import java.util.ArrayList;
64 import java.util.Collections;
65 import java.util.GregorianCalendar;
66 import java.util.List;
67 import java.util.Map;
68
69 import javax.servlet.http.Cookie;
70 import javax.servlet.http.HttpServletRequest;
71 import javax.servlet.http.HttpServletResponse;
72
73
74 public class BrowserBindingUtils
75 {
76 public static final String JSON_MIME_TYPE = "application/json";
77 public static final String HTML_MIME_TYPE = "text/html";
78 public static final String ROOT_PATH_FRAGMENT = "root";
79 public static final String CONTEXT_OBJECT_ID = "org.apache.chemistry.opencmis.browserbinding.objectId";
80 public static final String CONTEXT_OBJECT_TYPE_ID = "org.apache.chemistry.opencmis.browserbinding.objectTypeId";
81 public static final String CONTEXT_BASETYPE_ID = "org.apache.chemistry.opencmis.browserbinding.basetypeId";
82 public static final String CONTEXT_TRANSACTION = "org.apache.chemistry.opencmis.browserbinding.transaction";
83
84
85 private BrowserBindingUtils( )
86 {
87 }
88
89
90
91
92 public static UrlBuilder compileBaseUrl( HttpServletRequest request )
93 {
94 UrlBuilder url = new UrlBuilder( request.getScheme( ), request.getServerName( ), request.getServerPort( ),
95 null );
96
97 url.addPath( request.getContextPath( ) );
98 url.addPath( request.getServletPath( ) );
99
100 return url;
101 }
102
103 public static UrlBuilder compileRepositoryUrl( HttpServletRequest request, String repositoryId )
104 {
105 return compileBaseUrl( request ).addPathSegment( repositoryId );
106 }
107
108 public static UrlBuilder compileRootUrl( HttpServletRequest request, String repositoryId )
109 {
110 return compileRepositoryUrl( request, repositoryId ).addPathSegment( ROOT_PATH_FRAGMENT );
111 }
112
113
114
115
116 public static String getPath( HttpServletRequest request )
117 {
118 String[] pathFragments = HttpUtils.splitPath( request );
119
120 if ( pathFragments.length < 2 )
121 {
122 return null;
123 }
124
125 if ( pathFragments.length == 2 )
126 {
127 return "/";
128 }
129
130 StringBuilder sb = new StringBuilder( );
131
132 for ( int i = 2; i < pathFragments.length; i++ )
133 {
134 if ( pathFragments[i].length( ) == 0 )
135 {
136 continue;
137 }
138
139 sb.append( "/" );
140 sb.append( pathFragments[i] );
141 }
142
143 return sb.toString( );
144 }
145
146
147
148
149 public static void prepareContext( CallContext context, CallUrl callUrl, CmisService service, String repositoryId,
150 String objectId, String transaction, HttpServletRequest request )
151 {
152 CallContextImpl contextImpl = null;
153
154 if ( context instanceof CallContextImpl )
155 {
156 contextImpl = (CallContextImpl) context;
157 contextImpl.put( CONTEXT_TRANSACTION, transaction );
158 }
159
160 if ( callUrl != CallUrl.ROOT )
161 {
162 return;
163 }
164
165 ObjectData object = null;
166
167 if ( objectId != null )
168 {
169 object = service.getObject( repositoryId, objectId, "cmis:objectId,cmis:objectTypeId,cmis:baseTypeId",
170 false, IncludeRelationships.NONE, "cmis:none", false, false, null );
171 }
172 else
173 {
174 object = service.getObjectByPath( repositoryId, getPath( request ),
175 "cmis:objectId,cmis:objectTypeId,cmis:baseTypeId", false, IncludeRelationships.NONE, "cmis:none",
176 false, false, null );
177 }
178
179 if ( contextImpl != null )
180 {
181 contextImpl.put( CONTEXT_OBJECT_ID, object.getId( ) );
182 contextImpl.put( CONTEXT_OBJECT_TYPE_ID, getProperty( object, PropertyIds.OBJECT_TYPE_ID, String.class ) );
183 contextImpl.put( CONTEXT_BASETYPE_ID, getProperty( object, PropertyIds.BASE_TYPE_ID, String.class ) );
184 }
185 }
186
187
188
189
190 @SuppressWarnings( "unchecked" )
191 public static <T> T getProperty( ObjectData object, String name, Class<T> clazz )
192 {
193 if ( object == null )
194 {
195 return null;
196 }
197
198 Properties propData = object.getProperties( );
199
200 if ( propData == null )
201 {
202 return null;
203 }
204
205 Map<String, PropertyData<?>> properties = propData.getProperties( );
206
207 if ( properties == null )
208 {
209 return null;
210 }
211
212 PropertyData<?> property = properties.get( name );
213
214 if ( property == null )
215 {
216 return null;
217 }
218
219 Object value = property.getFirstValue( );
220
221 if ( !clazz.isInstance( value ) )
222 {
223 return null;
224 }
225
226 return (T) value;
227 }
228
229 public static Properties createProperties( ControlParser controlParser, String typeId, TypeCache typeCache )
230 {
231 List<String> propertyIds = controlParser.getValues( Constants.CONTROL_PROP_ID );
232
233 if ( propertyIds == null )
234 {
235 return null;
236 }
237
238 Map<Integer, String> singleValuePropertyMap = controlParser.getOneDimMap( Constants.CONTROL_PROP_VALUE );
239 Map<Integer, Map<Integer, String>> multiValuePropertyMap = controlParser.getTwoDimMap( Constants.CONTROL_PROP_VALUE );
240
241 if ( typeId == null )
242 {
243
244 int i = 0;
245
246 for ( String propertId : propertyIds )
247 {
248 if ( PropertyIds.OBJECT_TYPE_ID.equals( propertId ) )
249 {
250 typeId = singleValuePropertyMap.get( i );
251
252 break;
253 }
254
255 i++;
256 }
257
258 if ( typeId == null )
259 {
260 throw new CmisInvalidArgumentException( PropertyIds.OBJECT_TYPE_ID + " not set!" );
261 }
262 }
263
264 TypeDefinition typeDef = typeCache.getTypeDefinition( typeId );
265
266 if ( typeDef == null )
267 {
268 throw new CmisInvalidArgumentException( "Invalid type: " + typeId );
269 }
270
271 PropertiesImpl result = new PropertiesImpl( );
272
273 int i = 0;
274
275 for ( String propertyId : propertyIds )
276 {
277 PropertyDefinition<?> propDef = typeDef.getPropertyDefinitions( ).get( propertyId );
278
279 if ( propDef == null )
280 {
281 throw new CmisInvalidArgumentException( propertyId + " is unknown!" );
282 }
283
284 PropertyData<?> propertyData = null;
285
286 if ( ( singleValuePropertyMap != null ) && singleValuePropertyMap.containsKey( i ) )
287 {
288 propertyData = createPropertyData( propDef, singleValuePropertyMap.get( i ) );
289 }
290 else if ( ( multiValuePropertyMap != null ) && multiValuePropertyMap.containsKey( i ) )
291 {
292 propertyData = createPropertyData( propDef, controlParser.getValues( Constants.CONTROL_PROP_VALUE, i ) );
293 }
294 else
295 {
296 propertyData = createPropertyData( propDef, null );
297 }
298
299 result.addProperty( propertyData );
300
301 i++;
302 }
303
304 return result;
305 }
306
307 @SuppressWarnings( "unchecked" )
308 private static PropertyData<?> createPropertyData( PropertyDefinition<?> propDef, Object value )
309 {
310 List<String> strValues;
311
312 if ( value == null )
313 {
314 strValues = Collections.emptyList( );
315 }
316 else if ( value instanceof String )
317 {
318 strValues = new ArrayList<String>( );
319 strValues.add( (String) value );
320 }
321 else
322 {
323 strValues = (List<String>) value;
324 }
325
326 PropertyData<?> propertyData = null;
327
328 switch ( propDef.getPropertyType( ) )
329 {
330 case STRING:
331 propertyData = new PropertyStringImpl( propDef.getId( ), strValues );
332
333 break;
334
335 case ID:
336 propertyData = new PropertyIdImpl( propDef.getId( ), strValues );
337
338 break;
339
340 case BOOLEAN:
341
342 List<Boolean> boolValues = new ArrayList<Boolean>( strValues.size( ) );
343
344 try
345 {
346 for ( String s : strValues )
347 {
348 boolValues.add( Boolean.valueOf( s ) );
349 }
350 }
351 catch ( NumberFormatException e )
352 {
353 throw new CmisInvalidArgumentException( propDef.getId( ) + " value is not a boolean value!" );
354 }
355
356 propertyData = new PropertyBooleanImpl( propDef.getId( ), boolValues );
357
358 break;
359
360 case INTEGER:
361
362 List<BigInteger> intValues = new ArrayList<BigInteger>( strValues.size( ) );
363
364 try
365 {
366 for ( String s : strValues )
367 {
368 intValues.add( new BigInteger( s ) );
369 }
370 }
371 catch ( NumberFormatException e )
372 {
373 throw new CmisInvalidArgumentException( propDef.getId( ) + " value is not an integer value!" );
374 }
375
376 propertyData = new PropertyIntegerImpl( propDef.getId( ), intValues );
377
378 break;
379
380 case DECIMAL:
381
382 List<BigDecimal> decValues = new ArrayList<BigDecimal>( strValues.size( ) );
383
384 try
385 {
386 for ( String s : strValues )
387 {
388 decValues.add( new BigDecimal( s ) );
389 }
390 }
391 catch ( NumberFormatException e )
392 {
393 throw new CmisInvalidArgumentException( propDef.getId( ) + " value is not an integer value!" );
394 }
395
396 propertyData = new PropertyDecimalImpl( propDef.getId( ), decValues );
397
398 break;
399
400 case DATETIME:
401
402 List<GregorianCalendar> calValues = new ArrayList<GregorianCalendar>( strValues.size( ) );
403
404 try
405 {
406 for ( String s : strValues )
407 {
408 GregorianCalendar cal = new GregorianCalendar( );
409 cal.setTimeInMillis( Long.parseLong( s ) );
410 calValues.add( cal );
411 }
412 }
413 catch ( NumberFormatException e )
414 {
415 throw new CmisInvalidArgumentException( propDef.getId( ) + " value is not an datetime value!" );
416 }
417
418 propertyData = new PropertyDateTimeImpl( propDef.getId( ), calValues );
419
420 break;
421
422 case HTML:
423 propertyData = new PropertyHtmlImpl( propDef.getId( ), strValues );
424
425 break;
426
427 case URI:
428 propertyData = new PropertyUriImpl( propDef.getId( ), strValues );
429
430 break;
431 }
432
433 return propertyData;
434 }
435
436 public static List<String> createPolicies( ControlParser controlParser )
437 {
438 return controlParser.getValues( Constants.CONTROL_POLICY );
439 }
440
441 public static Acl createAddAcl( ControlParser controlParser )
442 {
443 List<String> principals = controlParser.getValues( Constants.CONTROL_ADD_ACE_PRINCIPAL );
444
445 if ( principals == null )
446 {
447 return null;
448 }
449
450 List<Ace> aces = new ArrayList<Ace>( );
451
452 int i = 0;
453
454 for ( String principalId : principals )
455 {
456 aces.add( new AccessControlEntryImpl( new AccessControlPrincipalDataImpl( principalId ),
457 controlParser.getValues( Constants.CONTROL_ADD_ACE_PERMISSION, i ) ) );
458 i++;
459 }
460
461 return new AccessControlListImpl( aces );
462 }
463
464 public static Acl createRemoveAcl( ControlParser controlParser )
465 {
466 List<String> principals = controlParser.getValues( Constants.CONTROL_REMOVE_ACE_PRINCIPAL );
467
468 if ( principals == null )
469 {
470 return null;
471 }
472
473 List<Ace> aces = new ArrayList<Ace>( );
474
475 int i = 0;
476
477 for ( String principalId : principals )
478 {
479 aces.add( new AccessControlEntryImpl( new AccessControlPrincipalDataImpl( principalId ),
480 controlParser.getValues( Constants.CONTROL_REMOVE_ACE_PERMISSION, i ) ) );
481 i++;
482 }
483
484 return new AccessControlListImpl( aces );
485 }
486
487 public static ContentStream createContentStream( HttpServletRequest request )
488 {
489 ContentStreamImpl result = null;
490
491 if ( request instanceof POSTHttpServletRequestWrapper )
492 {
493 POSTHttpServletRequestWrapper post = (POSTHttpServletRequestWrapper) request;
494
495 if ( post.getStream( ) != null )
496 {
497 result = new ContentStreamImpl( post.getFilename( ), post.getSize( ), post.getContentType( ),
498 post.getStream( ) );
499 }
500 }
501
502 return result;
503 }
504
505 protected static ObjectData getSimpleObject( CmisService service, String repositoryId, String objectId )
506 {
507 return service.getObject( repositoryId, objectId, null, false, IncludeRelationships.NONE, "cmis:none", false,
508 false, null );
509 }
510
511
512
513
514
515 public static void setStatus( HttpServletRequest request, HttpServletResponse response, int statusCode )
516 {
517 if ( getBooleanParameter( request, Constants.PARAM_SUPPRESS_RESPONSE_CODES, false ) )
518 {
519 statusCode = HttpServletResponse.SC_OK;
520 }
521
522 response.setStatus( statusCode );
523 }
524
525
526
527
528 public static String getCookieName( String transaction )
529 {
530 if ( ( transaction == null ) || ( transaction.length( ) == 0 ) )
531 {
532 return "cmis%";
533 }
534
535 return "cmis_" + Base64.encodeBytes( transaction.getBytes( ) ).replace( '=', '%' );
536 }
537
538
539
540
541 public static void setCookie( HttpServletRequest request, HttpServletResponse response, String repositoryId,
542 String transaction, String value )
543 {
544 setCookie( request, response, repositoryId, transaction, value, 3600 );
545 }
546
547
548
549
550 public static void deleteCookie( HttpServletRequest request, HttpServletResponse response, String repositoryId,
551 String transaction )
552 {
553 setCookie( request, response, repositoryId, transaction, "", 0 );
554 }
555
556
557
558
559 public static void setCookie( HttpServletRequest request, HttpServletResponse response, String repositoryId,
560 String transaction, String value, int expiry )
561 {
562 if ( ( transaction != null ) && ( transaction.length( ) > 0 ) )
563 {
564 Cookie transactionCookie = new Cookie( getCookieName( transaction ), value );
565 transactionCookie.setMaxAge( expiry );
566 transactionCookie.setPath( request.getContextPath( ) + request.getServletPath( ) + "/" + repositoryId );
567 response.addCookie( transactionCookie );
568 }
569 }
570
571 public static String createCookieValue( int code, String objectId, String ex, String message )
572 {
573 JSONObject result = new JSONObject( );
574
575 result.put( "code", code );
576 result.put( "objectId", ( objectId == null ) ? "" : objectId );
577 result.put( "exception", ( ex == null ) ? "" : ex );
578 result.put( "message", ( message == null ) ? "" : message );
579
580 return result.toJSONString( );
581 }
582
583
584
585
586
587 public static void writeJSON( JSONStreamAware json, HttpServletRequest request, HttpServletResponse response )
588 throws IOException
589 {
590 String transaction = getStringParameter( request, Constants.PARAM_TRANSACTION );
591
592 if ( transaction == null )
593 {
594 response.setContentType( JSON_MIME_TYPE );
595 response.setCharacterEncoding( "UTF-8" );
596
597 String callback = getStringParameter( request, Constants.PARAM_CALLBACK );
598
599 if ( callback != null )
600 {
601 if ( !callback.matches( "[A-Za-z0-9._\\[\\]]*" ) )
602 {
603 throw new CmisInvalidArgumentException( "Invalid callback name!" );
604 }
605
606 response.getWriter( ).print( callback + "(" );
607 }
608
609 json.writeJSONString( response.getWriter( ) );
610
611 if ( callback != null )
612 {
613 response.getWriter( ).print( ");" );
614 }
615 }
616 else
617 {
618 response.setContentType( HTML_MIME_TYPE );
619 response.setContentLength( 0 );
620 }
621
622 response.getWriter( ).flush( );
623 }
624
625 public static void writeEmpty( HttpServletRequest request, HttpServletResponse response )
626 throws IOException
627 {
628 response.setContentLength( 0 );
629 response.setContentType( "text/plain" );
630 response.getWriter( ).flush( );
631 }
632 public enum CallUrl
633 {SERVICE,
634 REPOSITORY,
635 ROOT;
636 }
637 }