001/*
002 * (C) Copyright 2008 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Thierry Delprat
018 */
019package org.nuxeo.ecm.platform.indexing.gateway.ws;
020
021import java.io.Serializable;
022import java.util.ArrayList;
023import java.util.List;
024import java.util.Map;
025import java.util.concurrent.ConcurrentHashMap;
026import java.util.concurrent.TimeUnit;
027import java.util.concurrent.locks.ReentrantLock;
028
029import javax.jws.WebMethod;
030import javax.jws.WebParam;
031import javax.jws.WebService;
032import javax.jws.soap.SOAPBinding;
033import javax.jws.soap.SOAPBinding.Style;
034
035import org.apache.commons.logging.Log;
036import org.apache.commons.logging.LogFactory;
037import org.nuxeo.common.utils.ExceptionUtils;
038import org.nuxeo.ecm.core.api.CoreSession;
039import org.nuxeo.ecm.core.api.DocumentModel;
040import org.nuxeo.ecm.core.api.IdRef;
041import org.nuxeo.ecm.core.api.IterableQueryResult;
042import org.nuxeo.ecm.core.api.PathRef;
043import org.nuxeo.ecm.core.api.security.ACL;
044import org.nuxeo.ecm.core.api.security.ACP;
045import org.nuxeo.ecm.core.query.sql.NXQL;
046import org.nuxeo.ecm.core.schema.DocumentType;
047import org.nuxeo.ecm.core.schema.SchemaManager;
048import org.nuxeo.ecm.platform.api.ws.DocumentBlob;
049import org.nuxeo.ecm.platform.api.ws.DocumentDescriptor;
050import org.nuxeo.ecm.platform.api.ws.DocumentProperty;
051import org.nuxeo.ecm.platform.api.ws.DocumentSnapshot;
052import org.nuxeo.ecm.platform.api.ws.NuxeoRemoting;
053import org.nuxeo.ecm.platform.api.ws.WsACE;
054import org.nuxeo.ecm.platform.api.ws.session.WSRemotingSession;
055import org.nuxeo.ecm.platform.audit.ws.EventDescriptorPage;
056import org.nuxeo.ecm.platform.audit.ws.ModifiedDocumentDescriptor;
057import org.nuxeo.ecm.platform.audit.ws.ModifiedDocumentDescriptorPage;
058import org.nuxeo.ecm.platform.audit.ws.WSAuditBean;
059import org.nuxeo.ecm.platform.audit.ws.api.WSAudit;
060import org.nuxeo.ecm.platform.indexing.gateway.adapter.IndexingAdapter;
061import org.nuxeo.ecm.platform.indexing.gateway.ws.api.WSIndexingGateway;
062import org.nuxeo.ecm.platform.ws.AbstractNuxeoWebService;
063import org.nuxeo.ecm.platform.ws.NuxeoRemotingBean;
064import org.nuxeo.runtime.api.Framework;
065
066/**
067 * Base class for WS beans used for external indexers. Implements most of NuxeoRemotingBean trying as hard as possible
068 * no to throw when a requested document is missing but returning empty descriptions instead so as to
069 * make external indexers not view recently deleted documents as applicative errors.
070 *
071 * @author tiry
072 */
073@WebService(name = "WSIndexingGatewayInterface", serviceName = "WSIndexingGatewayService")
074@SOAPBinding(style = Style.DOCUMENT)
075public class WSIndexingGatewayBean extends AbstractNuxeoWebService implements WSIndexingGateway {
076
077    protected static final String ENFORCE_SYNC_PROP_NAME = "nuxeo.indexing.gateway.forceSync";
078
079    protected static Log log = LogFactory.getLog(WSIndexingGatewayBean.class);
080
081    private static final long serialVersionUID = 4696352633818100451L;
082
083    protected transient WSAudit auditBean;
084
085    protected transient NuxeoRemoting platformRemoting;
086
087    protected IndexingAdapter adapter;
088
089    protected ConcurrentHashMap<String, ReentrantLock> sessionIdLocks = new ConcurrentHashMap<String, ReentrantLock>();
090
091    protected Boolean enforceSync = null;
092
093    protected boolean forceSync() {
094        if (enforceSync == null) {
095            String value = Framework.getProperty(ENFORCE_SYNC_PROP_NAME, null);
096            enforceSync = false;
097            if (value != null) {
098                enforceSync = Boolean.parseBoolean(value);
099            } else {
100                enforceSync = false;
101            }
102        }
103        return enforceSync;
104    }
105
106    protected void lockSession(String sid) {
107        if (forceSync()) {
108            ReentrantLock lock = sessionIdLocks.putIfAbsent(sid, new ReentrantLock());
109            boolean aquired = false;
110            if (lock == null) {
111                lock = sessionIdLocks.get(sid);
112            }
113            try {
114                aquired = lock.tryLock(10, TimeUnit.SECONDS);
115            } catch (InterruptedException e) {
116                ExceptionUtils.checkInterrupt(e);
117            }
118            if (!aquired) {
119                log.error("Failed to acquire lock (timeout) for sid " + sid);
120            }
121        }
122    }
123
124    protected void releaseSession(String sid) {
125        if (forceSync()) {
126            ReentrantLock lock = sessionIdLocks.get(sid);
127            if (lock != null) {
128                lock.unlock();
129            }
130        }
131    }
132
133    protected WSAudit getWSAudit() {
134        if (auditBean == null) {
135            auditBean = new WSAuditBean();
136        }
137        return auditBean;
138    }
139
140    protected NuxeoRemoting getWSNuxeoRemoting() {
141        if (platformRemoting == null) {
142            platformRemoting = new NuxeoRemotingBean();
143        }
144        return platformRemoting;
145    }
146
147    protected IndexingAdapter getAdapter() {
148        if (adapter == null) {
149            adapter = Framework.getLocalService(IndexingAdapter.class);
150        }
151        return adapter;
152    }
153
154    @WebMethod
155    public DocumentDescriptor[] getChildren(@WebParam(name = "sessionId") String sessionId,
156            @WebParam(name = "uuid") String uuid) {
157        try {
158            lockSession(sessionId);
159            CoreSession session = initSession(sessionId).getDocumentManager();
160            if (session.exists(new IdRef(uuid))) {
161                return getWSNuxeoRemoting().getChildren(sessionId, uuid);
162            } else {
163                return new DocumentDescriptor[0];
164            }
165        } finally {
166            releaseSession(sessionId);
167        }
168    }
169
170    @WebMethod
171    public DocumentDescriptor getCurrentVersion(@WebParam(name = "sessionId") String sid,
172            @WebParam(name = "uuid") String uid) {
173        try {
174            lockSession(sid);
175            CoreSession session = initSession(sid).getDocumentManager();
176            if (session.exists(new IdRef(uid))) {
177                return getWSNuxeoRemoting().getCurrentVersion(sid, uid);
178            } else {
179                return missingDocumentDescriptor(uid);
180            }
181        } finally {
182            releaseSession(sid);
183        }
184    }
185
186    @WebMethod
187    public DocumentDescriptor getDocument(@WebParam(name = "sessionId") String sessionId,
188            @WebParam(name = "uuid") String uuid) {
189        try {
190            lockSession(sessionId);
191            CoreSession session = initSession(sessionId).getDocumentManager();
192            DocumentDescriptor dd;
193            if (session.exists(new IdRef(uuid))) {
194                dd = getWSNuxeoRemoting().getDocument(sessionId, uuid);
195            } else {
196                dd = missingDocumentDescriptor(uuid);
197            }
198            return getAdapter().adaptDocumentDescriptor(session, uuid, dd);
199        } finally {
200            releaseSession(sessionId);
201        }
202    }
203
204    @WebMethod
205    public WsACE[] getDocumentACL(@WebParam(name = "sessionId") String sid, @WebParam(name = "uuid") String uuid)
206            {
207        try {
208            lockSession(sid);
209            CoreSession session = initSession(sid).getDocumentManager();
210            WsACE[] aces;
211            if (session.exists(new IdRef(uuid))) {
212                aces = getWSNuxeoRemoting().getDocumentACL(sid, uuid);
213            } else {
214                aces = new WsACE[0];
215            }
216            return getAdapter().adaptDocumentACL(session, uuid, aces);
217        } finally {
218            releaseSession(sid);
219        }
220    }
221
222    @WebMethod
223    public WsACE[] getDocumentLocalACL(@WebParam(name = "sessionId") String sid, @WebParam(name = "uuid") String uuid)
224            {
225        try {
226            lockSession(sid);
227            CoreSession session = initSession(sid).getDocumentManager();
228            WsACE[] aces;
229            if (session.exists(new IdRef(uuid))) {
230                aces = getWSNuxeoRemoting().getDocumentLocalACL(sid, uuid);
231            } else {
232                aces = new WsACE[0];
233            }
234            return getAdapter().adaptDocumentLocalACL(session, uuid, aces);
235        } finally {
236            releaseSession(sid);
237        }
238    }
239
240    public DocumentBlob[] getDocumentBlobsExt(@WebParam(name = "sessionId") String sid,
241            @WebParam(name = "uuid") String uuid, @WebParam(name = "useDownloadUrl") boolean useDownloadUrl)
242            {
243        try {
244            lockSession(sid);
245            CoreSession session = initSession(sid).getDocumentManager();
246            DocumentBlob[] blobs;
247            if (session.exists(new IdRef(uuid))) {
248                blobs = getWSNuxeoRemoting().getDocumentBlobsExt(sid, uuid, useDownloadUrl);
249            } else {
250                blobs = new DocumentBlob[0];
251            }
252            return getAdapter().adaptDocumentBlobs(session, uuid, blobs);
253
254        } finally {
255            releaseSession(sid);
256        }
257    }
258
259    @WebMethod
260    public DocumentBlob[] getDocumentBlobs(@WebParam(name = "sessionId") String sid,
261            @WebParam(name = "uuid") String uuid) {
262        return getDocumentBlobsExt(sid, uuid, getAdapter().useDownloadUrlForBlob());
263    }
264
265    @WebMethod
266    public DocumentProperty[] getDocumentNoBlobProperties(@WebParam(name = "sessionId") String sid,
267            @WebParam(name = "uuid") String uuid) {
268
269        try {
270            lockSession(sid);
271            CoreSession session = initSession(sid).getDocumentManager();
272            DocumentProperty[] properties;
273            if (session.exists(new IdRef(uuid))) {
274                properties = getWSNuxeoRemoting().getDocumentNoBlobProperties(sid, uuid);
275            } else {
276                properties = new DocumentProperty[0];
277            }
278            return getAdapter().adaptDocumentNoBlobProperties(session, uuid, properties);
279        } finally {
280            releaseSession(sid);
281        }
282
283    }
284
285    @WebMethod
286    public DocumentProperty[] getDocumentProperties(@WebParam(name = "sessionId") String sid,
287            @WebParam(name = "uuid") String uuid) {
288        try {
289            lockSession(sid);
290            CoreSession session = initSession(sid).getDocumentManager();
291            DocumentProperty[] properties;
292            if (session.exists(new IdRef(uuid))) {
293                properties = getWSNuxeoRemoting().getDocumentProperties(sid, uuid);
294            } else {
295                properties = new DocumentProperty[0];
296            }
297            return getAdapter().adaptDocumentProperties(session, uuid, properties);
298        } finally {
299            releaseSession(sid);
300        }
301    }
302
303    @WebMethod
304    public String[] getGroups(@WebParam(name = "sessionId") String sid,
305            @WebParam(name = "parentGroup") String parentGroup) {
306        return getWSNuxeoRemoting().getGroups(sid, parentGroup);
307    }
308
309    @WebMethod
310    public String getRepositoryName(@WebParam(name = "sessionId") String sid) {
311        return getWSNuxeoRemoting().getRepositoryName(sid);
312    }
313
314    @WebMethod
315    public DocumentDescriptor getRootDocument(@WebParam(name = "sessionId") String sessionId) {
316        try {
317            lockSession(sessionId);
318            return getWSNuxeoRemoting().getRootDocument(sessionId);
319        } finally {
320            releaseSession(sessionId);
321        }
322    }
323
324    @WebMethod
325    public String resolvePathToUUID(@WebParam(name = "sessionId") String sessionId, @WebParam(name = "path") String path)
326            {
327        try {
328            lockSession(sessionId);
329            CoreSession session = initSession(sessionId).getDocumentManager();
330            if (session != null) {
331                PathRef pathRef = new PathRef(path);
332                if (session.exists(pathRef)) {
333                    return session.getDocument(pathRef).getId();
334                }
335            }
336            return null;
337        } finally {
338            releaseSession(sessionId);
339        }
340    }
341
342    @WebMethod
343    public UUIDPage getRecursiveChildrenUUIDsByPage(@WebParam(name = "sessionId") String sid,
344            @WebParam(name = "uuid") String uuid, @WebParam(name = "page") int page,
345            @WebParam(name = "pageSize") int pageSize) {
346
347        try {
348            lockSession(sid);
349            CoreSession session = initSession(sid).getDocumentManager();
350
351            List<String> uuids = new ArrayList<String>();
352            IdRef parentRef = new IdRef(uuid);
353            DocumentModel parent = session.getDocument(parentRef);
354            String path = parent.getPathAsString();
355
356            String query = "select ecm:uuid from Document where ecm:path startswith '" + path + "' order by ecm:uuid";
357
358            IterableQueryResult result = session.queryAndFetch(query, "NXQL");
359            boolean hasMore = false;
360            try {
361                if (page > 1) {
362                    int skip = (page - 1) * pageSize;
363                    result.skipTo(skip);
364                }
365
366                for (Map<String, Serializable> record : result) {
367                    uuids.add((String) record.get(NXQL.ECM_UUID));
368                    if (uuids.size() == pageSize) {
369                        hasMore = true;
370                        break;
371                    }
372                }
373            } finally {
374                result.close();
375            }
376            return new UUIDPage(uuids.toArray(new String[uuids.size()]), page, hasMore);
377        } finally {
378            releaseSession(sid);
379        }
380
381    }
382
383    @WebMethod
384    public String[] getRecursiveChildrenUUIDs(@WebParam(name = "sessionId") String sid,
385            @WebParam(name = "uuid") String uuid) {
386
387        try {
388            lockSession(sid);
389            CoreSession session = initSession(sid).getDocumentManager();
390
391            List<String> uuids = new ArrayList<String>();
392            IdRef parentRef = new IdRef(uuid);
393            DocumentModel parent = session.getDocument(parentRef);
394            String path = parent.getPathAsString();
395
396            String query = "select ecm:uuid from Document where ecm:path startswith '" + path + "' order by ecm:uuid";
397
398            IterableQueryResult result = session.queryAndFetch(query, "NXQL");
399
400            try {
401                for (Map<String, Serializable> record : result) {
402                    uuids.add((String) record.get(NXQL.ECM_UUID));
403                }
404            } finally {
405                result.close();
406            }
407
408            return uuids.toArray(new String[uuids.size()]);
409        } finally {
410            releaseSession(sid);
411        }
412
413    }
414
415    @WebMethod
416    public DocumentTypeDescriptor[] getTypeDefinitions() {
417
418        List<DocumentTypeDescriptor> result = new ArrayList<DocumentTypeDescriptor>();
419        SchemaManager sm = Framework.getService(SchemaManager.class);
420
421        for (DocumentType dt : sm.getDocumentTypes()) {
422            result.add(new DocumentTypeDescriptor(dt));
423        }
424
425        return result.toArray(new DocumentTypeDescriptor[result.size()]);
426    }
427
428    @WebMethod
429    public DocumentDescriptor getDocumentFromPath(@WebParam(name = "sessionId") String sessionId,
430            @WebParam(name = "path") String path) {
431        try {
432            lockSession(sessionId);
433            String uuid = resolvePathToUUID(sessionId, path);
434            if (uuid != null) {
435                return getWSNuxeoRemoting().getDocument(sessionId, uuid);
436            } else {
437                // should we return a missing document with an null uuid
438                // instead?
439                return null;
440            }
441        } finally {
442            releaseSession(sessionId);
443        }
444    }
445
446    @WebMethod
447    public DocumentDescriptor getSourceDocument(@WebParam(name = "sessionId") String sid,
448            @WebParam(name = "uuid") String uid) {
449        try {
450            lockSession(sid);
451            CoreSession session = initSession(sid).getDocumentManager();
452            if (session.exists(new IdRef(uid))) {
453                return getWSNuxeoRemoting().getSourceDocument(sid, uid);
454            } else {
455                return missingDocumentDescriptor(uid);
456            }
457        } finally {
458            releaseSession(sid);
459        }
460    }
461
462    @WebMethod
463    public String[] getUsers(@WebParam(name = "sessionId") String sid,
464            @WebParam(name = "parentGroup") String parentGroup) {
465        return getWSNuxeoRemoting().getUsers(sid, parentGroup);
466    }
467
468    @WebMethod
469    public DocumentDescriptor[] getVersions(@WebParam(name = "sessionId") String sid,
470            @WebParam(name = "uuid") String uid) {
471        try {
472            lockSession(sid);
473            CoreSession session = initSession(sid).getDocumentManager();
474            if (session.exists(new IdRef(uid))) {
475                return getWSNuxeoRemoting().getVersions(sid, uid);
476            } else {
477                return new DocumentDescriptor[0];
478            }
479        } finally {
480            releaseSession(sid);
481        }
482    }
483
484    @WebMethod
485    public String[] listGroups(@WebParam(name = "sessionId") String sid, @WebParam(name = "from") int from,
486            @WebParam(name = "to") int to) {
487        return getWSNuxeoRemoting().listGroups(sid, from, to);
488    }
489
490    @WebMethod
491    public String[] listUsers(@WebParam(name = "sessionId") String sid, @WebParam(name = "from") int from,
492            @WebParam(name = "to") int to) {
493        return getWSNuxeoRemoting().listUsers(sid, from, to);
494    }
495
496    @WebMethod
497    public ModifiedDocumentDescriptor[] listModifiedDocuments(@WebParam(name = "sessionId") String sessionId,
498            @WebParam(name = "dateRangeQuery") String dateRangeQuery) {
499        return getWSAudit().listModifiedDocuments(sessionId, dateRangeQuery);
500    }
501
502    @WebMethod
503    public ModifiedDocumentDescriptorPage listModifiedDocumentsByPage(@WebParam(name = "sessionId") String sessionId,
504            @WebParam(name = "dateRangeQuery") String dateRangeQuery, @WebParam(name = "path") String path,
505            @WebParam(name = "page") int page, @WebParam(name = "pageSize") int pageSize) {
506        return getWSAudit().listModifiedDocumentsByPage(sessionId, dateRangeQuery, path, page, pageSize);
507    }
508
509    @WebMethod
510    public EventDescriptorPage listEventsByPage(@WebParam(name = "sessionId") String sessionId,
511            @WebParam(name = "dateRangeQuery") String dateRangeQuery, @WebParam(name = "page") int page,
512            @WebParam(name = "pageSize") int pageSize) {
513        return getWSAudit().listEventsByPage(sessionId, dateRangeQuery, page, pageSize);
514    }
515
516    @WebMethod
517    public EventDescriptorPage listDocumentEventsByPage(@WebParam(name = "sessionId") String sessionId,
518            @WebParam(name = "dateRangeQuery") String dateRangeQuery, @WebParam(name = "startDate") String startDate,
519            @WebParam(name = "path") String path, @WebParam(name = "page") int page,
520            @WebParam(name = "pageSize") int pageSize) {
521        return getWSAudit().listDocumentEventsByPage(sessionId, dateRangeQuery, startDate, path, page, pageSize);
522    }
523
524    @WebMethod
525    public String getRelativePathAsString(@WebParam(name = "sessionId") String sessionId,
526            @WebParam(name = "uuid") String uuid) {
527        try {
528            lockSession(sessionId);
529            CoreSession session = initSession(sessionId).getDocumentManager();
530            if (session.exists(new IdRef(uuid))) {
531                return getWSNuxeoRemoting().getRelativePathAsString(sessionId, uuid);
532            } else {
533                return null;
534            }
535        } finally {
536            releaseSession(sessionId);
537        }
538    }
539
540    @WebMethod
541    public boolean hasPermission(@WebParam(name = "sessionId") String sid, @WebParam(name = "uuid") String uuid,
542            @WebParam(name = "permission") String permission) {
543        try {
544            lockSession(sid);
545            CoreSession session = initSession(sid).getDocumentManager();
546            if (session.exists(new IdRef(uuid))) {
547                return getWSNuxeoRemoting().hasPermission(sid, uuid, permission);
548            } else {
549                return false;
550            }
551        } finally {
552            releaseSession(sid);
553        }
554    }
555
556    @WebMethod
557    public String uploadDocument(@WebParam(name = "sessionId") String sid, String path, String type, String[] properties)
558            {
559        try {
560            lockSession(sid);
561            return getWSNuxeoRemoting().uploadDocument(sid, path, type, properties);
562        } finally {
563            releaseSession(sid);
564        }
565    }
566
567    @WebMethod
568    public String connect(@WebParam(name = "userName") String username, @WebParam(name = "password") String password)
569            {
570        return getWSNuxeoRemoting().connect(username, password);
571    }
572
573    @WebMethod
574    public void disconnect(@WebParam(name = "sessionId") String sid) {
575        getWSNuxeoRemoting().disconnect(sid);
576        if (forceSync()) {
577            ReentrantLock lock = sessionIdLocks.get(sid);
578            if (lock != null) {
579                if (lock.isLocked()) {
580                    lock.unlock();
581                }
582                sessionIdLocks.remove(sid);
583            }
584        }
585    }
586
587    @WebMethod
588    public EventDescriptorPage queryEventsByPage(@WebParam(name = "sessionId") String sessionId,
589            @WebParam(name = "whereClause") String whereClause, @WebParam(name = "pageIndex") int page,
590            @WebParam(name = "pageSize") int pageSize) {
591        return getWSAudit().queryEventsByPage(sessionId, whereClause, page, pageSize);
592    }
593
594    @WebMethod
595    public boolean validateUserPassword(@WebParam(name = "sessionId") String sessionId,
596            @WebParam(name = "username") String username, @WebParam(name = "password") String password)
597            {
598        WSRemotingSession rs = initSession(sessionId);
599        return rs.getUserManager().checkUsernamePassword(username, password);
600    }
601
602    @WebMethod
603    public String[] getUserGroups(@WebParam(name = "sessionId") String sessionId,
604            @WebParam(name = "username") String username) {
605        WSRemotingSession rs = initSession(sessionId);
606        List<String> groups = rs.getUserManager().getPrincipal(username).getAllGroups();
607        String[] groupArray = new String[groups.size()];
608        groups.toArray(groupArray);
609        return groupArray;
610    }
611
612    public DocumentSnapshot getDocumentSnapshotExt(@WebParam(name = "sessionId") String sessionId,
613            @WebParam(name = "uuid") String uuid, @WebParam(name = "useDownloadUrl") boolean useDownloadUrl)
614            {
615
616        try {
617            lockSession(sessionId);
618            WSRemotingSession rs = initSession(sessionId);
619            DocumentModel doc = rs.getDocumentManager().getDocument(new IdRef(uuid));
620
621            DocumentProperty[] props = getDocumentNoBlobProperties(sessionId, uuid);
622            DocumentBlob[] blobs = getDocumentBlobs(sessionId, uuid);
623
624            WsACE[] resACP = null;
625
626            ACP acp = doc.getACP();
627            if (acp != null && acp.getACLs().length > 0) {
628                ACL acl = acp.getMergedACLs("MergedACL");
629                resACP = WsACE.wrap(acl.getACEs());
630            }
631            DocumentSnapshot ds = new DocumentSnapshot(props, blobs, doc.getPathAsString(), resACP);
632            return ds;
633        } finally {
634            releaseSession(sessionId);
635        }
636    }
637
638    @WebMethod
639    public DocumentSnapshot getDocumentSnapshot(@WebParam(name = "sessionId") String sessionId,
640            @WebParam(name = "uuid") String uuid) {
641        return getDocumentSnapshotExt(sessionId, uuid, getAdapter().useDownloadUrlForBlob());
642    }
643
644    public ModifiedDocumentDescriptorPage listDeletedDocumentsByPage(@WebParam(name = "sessionId") String sessionId,
645            @WebParam(name = "dataRangeQuery") String dateRangeQuery, @WebParam(name = "docPath") String path,
646            @WebParam(name = "pageIndex") int page, @WebParam(name = "pageSize") int pageSize) {
647
648        return getWSAudit().listDeletedDocumentsByPage(sessionId, dateRangeQuery, path, page, pageSize);
649    }
650
651    /**
652     * Utility method to build descriptor for a document that is non longer to be found in the repository.
653     *
654     * @param uuid
655     * @return
656     */
657    protected DocumentDescriptor missingDocumentDescriptor(String uuid) {
658        // TODO: if we have to make the API / WSDL evolve it would be nice to
659        // include an explicit attribute in DocumentDescriptor to mark missing
660        // documents
661        DocumentDescriptor dd = new DocumentDescriptor();
662        dd.setUUID(uuid);
663        return dd;
664    }
665
666}