1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package org.xwiki.annotation.rest.internal

File AbstractAnnotationRESTResource.java

 

Coverage histogram

../../../../../img/srcFileCovDistChart6.png
69% of files have more coverage

Code metrics

16
94
8
1
331
180
18
0.19
11.75
8
2.25

Classes

Class Line # Actions
AbstractAnnotationRESTResource 60 94 0% 18 49
0.5847457658.5%
 

Contributing tests

No tests hitting this source file were found.

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20   
21    package org.xwiki.annotation.rest.internal;
22   
23    import java.io.PrintWriter;
24    import java.io.StringWriter;
25    import java.util.ArrayList;
26    import java.util.Collection;
27    import java.util.HashMap;
28    import java.util.List;
29    import java.util.Map;
30    import java.util.logging.Level;
31   
32    import javax.inject.Inject;
33   
34    import org.xwiki.annotation.Annotation;
35    import org.xwiki.annotation.AnnotationService;
36    import org.xwiki.annotation.AnnotationServiceException;
37    import org.xwiki.annotation.rest.model.jaxb.AnnotatedContent;
38    import org.xwiki.annotation.rest.model.jaxb.AnnotationField;
39    import org.xwiki.annotation.rest.model.jaxb.AnnotationRequest;
40    import org.xwiki.annotation.rest.model.jaxb.AnnotationResponse;
41    import org.xwiki.annotation.rest.model.jaxb.AnnotationStub;
42    import org.xwiki.annotation.rest.model.jaxb.ObjectFactory;
43    import org.xwiki.annotation.rights.AnnotationRightService;
44    import org.xwiki.context.Execution;
45    import org.xwiki.model.reference.DocumentReference;
46    import org.xwiki.rest.XWikiResource;
47   
48    import com.xpn.xwiki.XWiki;
49    import com.xpn.xwiki.XWikiContext;
50    import com.xpn.xwiki.XWikiException;
51    import com.xpn.xwiki.doc.XWikiDocument;
52    import com.xpn.xwiki.web.XWikiURLFactory;
53   
54    /**
55    * Base class for the annotation REST services, to implement common functionality to all annotation REST services.
56    *
57    * @version $Id: 301abd7330774ec02d0140d1950415b2a44f8b98 $
58    * @since 2.3M1
59    */
 
60    public abstract class AbstractAnnotationRESTResource extends XWikiResource
61    {
62    /**
63    * The default action to render the document for. <br>
64    * TODO: action should be obtained from the calling client in the parameters
65    */
66    protected static final String DEFAULT_ACTION = "view";
67   
68    /**
69    * The annotations service to be used by this REST interface.
70    */
71    @Inject
72    protected AnnotationService annotationService;
73   
74    /**
75    * The annotations rights checking service, to check user rights to perform annotations actions.
76    */
77    @Inject
78    protected AnnotationRightService annotationRightService;
79   
80    /**
81    * The execution needed to get the annotation author from the context user.
82    */
83    @Inject
84    protected Execution execution;
85   
86    /**
87    * Builds an annotation response containing the annotated content along with the annotation stubs, according to the
88    * requirements in the passed annotations request.
89    *
90    * @param request the annotations request
91    * @param documentName the name of the document to provide an annotated response for
92    * @return an annotation response with the annotated content and the annotation stubs
93    * @throws AnnotationServiceException in case something goes wrong handling the annotations
94    * @throws XWikiException in case something goes wrong manipulating the xwiki context & documents
95    */
 
96  8 toggle protected AnnotationResponse getSuccessResponseWithAnnotatedContent(String documentName, AnnotationRequest request)
97    throws AnnotationServiceException, XWikiException
98    {
99  8 ObjectFactory factory = new ObjectFactory();
100  8 AnnotationResponse response = factory.createAnnotationResponse();
101   
102    // get the annotations on this content
103  8 Collection<Annotation> annotations = annotationService.getAnnotations(documentName);
104    // filter them according to the request
105  8 Collection<Annotation> filteredAnnotations = filterAnnotations(annotations, request);
106    // render the document with the filtered annotations on it
107  8 String renderedHTML = renderDocumentWithAnnotations(documentName, null, DEFAULT_ACTION, filteredAnnotations);
108    // prepare the annotated content
109  8 AnnotatedContent annotatedContentResponse = factory.createAnnotatedContent();
110  8 annotatedContentResponse.getAnnotations().addAll(
111    prepareAnnotationStubsSet(filteredAnnotations, request.getRequest().getFields()));
112  8 annotatedContentResponse.setContent(renderedHTML);
113    // set the annotated content along with the return code in the response and return it
114  8 response.setAnnotatedContent(annotatedContentResponse);
115  8 response.setResponseCode(0);
116   
117  8 return response;
118    }
119   
120    /**
121    * Helper function to translate a collection of annotations from the {@link Annotation} model to the JAXB model to
122    * be serialized for REST communication.
123    *
124    * @param annotations the annotations collection to be translated
125    * @param requestedFields the extra parameters that should be set for the prepared annotations
126    * @return translate set of org.xwiki.annotation.internal.annotation.Annotation to set of
127    * org.xwiki.annotation.internal.annotation.Annotation
128    */
 
129  8 toggle private Collection<AnnotationStub> prepareAnnotationStubsSet(Collection<Annotation> annotations,
130    List<String> requestedFields)
131    {
132  8 ObjectFactory factory = new ObjectFactory();
133  8 List<AnnotationStub> set = new ArrayList<AnnotationStub>();
134  8 for (Annotation xwikiAnnotation : annotations) {
135  16 AnnotationStub annotation = factory.createAnnotationStub();
136  16 annotation.setAnnotationId(xwikiAnnotation.getId());
137  16 annotation.setState(xwikiAnnotation.getState().toString());
138    // for all the requested extra fields, get them from the annotation and send them
139  16 for (String extraField : requestedFields) {
140  0 Object value = xwikiAnnotation.get(extraField);
141  0 AnnotationField field = new AnnotationField();
142  0 field.setName(extraField);
143    // value.toString() by default, null if value is missing
144  0 field.setValue(value != null ? value.toString() : null);
145  0 annotation.getFields().add(field);
146    }
147  16 set.add(annotation);
148    }
149  8 return set;
150    }
151   
152    /**
153    * Helper function to create an error response from a passed exception. <br>
154    *
155    * @param exception the exception that was encountered during regular execution of service
156    * @return an error response
157    */
 
158  0 toggle protected AnnotationResponse getErrorResponse(Throwable exception)
159    {
160  0 AnnotationResponse result = new ObjectFactory().createAnnotationResponse();
161  0 result.setResponseCode(1);
162  0 String responseMessage = exception.getMessage();
163  0 if (responseMessage == null) {
164    // serialize the stack trace and send it as an error response
165  0 StringWriter stackTraceWriter = new StringWriter();
166  0 exception.printStackTrace(new PrintWriter(stackTraceWriter));
167  0 responseMessage = stackTraceWriter.toString();
168    }
169  0 result.setResponseMessage(responseMessage);
170  0 result.setAnnotatedContent(null);
171  0 return result;
172    }
173   
174    /**
175    * Helper function to get the rendered content of the document with annotations. All setup of context for rendering
176    * content similar to the rendering on standard view will be done in this function. <br>
177    * FIXME: find out if this whole context setup code has to be here or in the annotations service
178    *
179    * @param docName the name of the document to render
180    * @param language the language in which to render the document
181    * @param action the context action to render the document for
182    * @param annotations the annotations to render on the document
183    * @return the HTML rendered content of the document
184    * @throws XWikiException if anything wrong happens while setting up the context for rendering
185    * @throws AnnotationServiceException if anything goes wrong during the rendering of the annotations
186    */
 
187  8 toggle private String renderDocumentWithAnnotations(String docName, String language, String action,
188    Collection<Annotation> annotations) throws XWikiException, AnnotationServiceException
189    {
190  8 String isInRenderingEngineKey = "isInRenderingEngine";
191  8 XWikiContext context = this.xcontextProvider.get();
192  8 Object isInRenderingEngine = context.get(isInRenderingEngineKey);
193    // set the context url factory to the servlet url factory so that all links get correctly generated as if we
194    // were view-ing the page
195  8 XWikiURLFactory oldFactory = context.getURLFactory();
196  8 int oldMode = context.getMode();
197  8 String result = null;
198  8 try {
199  8 context.setMode(XWikiContext.MODE_SERVLET);
200  8 XWikiURLFactory urlf =
201    context.getWiki().getURLFactoryService().createURLFactory(context.getMode(), context);
202  8 context.setURLFactory(urlf);
203    // setup documents on the context, and velocity context, and message tool for i18n and all
204  8 setUpDocuments(docName, language);
205    // set the current action on the context
206  8 context.setAction(action);
207  8 context.put(isInRenderingEngineKey, true);
208    // render the content in xhtml syntax, with the passed list of annotations
209  8 result = annotationService.getAnnotatedRenderedContent(docName, null, "xhtml/1.0", annotations);
210    } finally {
211  8 if (isInRenderingEngine != null) {
212  0 context.put(isInRenderingEngineKey, isInRenderingEngine);
213    } else {
214  8 context.remove(isInRenderingEngineKey);
215    }
216  8 context.setURLFactory(oldFactory);
217  8 context.setMode(oldMode);
218    }
219  8 return result;
220    }
221   
222    /**
223    * Helper function to prepare the XWiki documents and translations on the context and velocity context. <br>
224    * TODO: check how this code could be written only once (not duplicate the prepareDocuments function in XWiki)
225    *
226    * @param docName the full name of the document to prepare context for
227    * @param language the language of the document
228    * @throws XWikiException if anything goes wrong accessing documents
229    */
 
230  8 toggle private void setUpDocuments(String docName, String language) throws XWikiException
231    {
232  8 XWikiContext context = xcontextProvider.get();
233  8 XWiki xwiki = context.getWiki();
234   
235    // prepare the messaging tools and set them on context
236  8 xwiki.prepareResources(context);
237   
238  8 XWikiDocument doc = xwiki.getDocument(docName, context);
239    // setup the xwiki context
240  8 context.put("doc", doc);
241  8 context.put("cdoc", doc);
242   
243  8 XWikiDocument tdoc = doc.getTranslatedDocument(language, context);
244  8 context.put("tdoc", tdoc);
245    // and render the xwikivars to have all the variables set ($has*, $blacklistedSpaces, etc)
246  8 context.getWiki().renderTemplate("xwikivars.vm", context);
247    }
248   
249    /**
250    * Helper method to filter a set of annotations according to the criteria in the passed annotation request. The
251    * fields in the filter of the request will be interpreted as a filter for equality with the value in the actual
252    * annotation, and all the fields conditions will be put together with an "or" operation.
253    *
254    * @param annotations the collection of annotations to filter
255    * @param request the request according which to filter
256    * @return the filtered collection of annotations
257    */
 
258  8 toggle protected Collection<Annotation> filterAnnotations(Collection<Annotation> annotations, AnnotationRequest request)
259    {
260  8 Collection<Annotation> result = new ArrayList<Annotation>();
261   
262  8 Map<String, List<String>> filters = new HashMap<String, List<String>>();
263  8 for (AnnotationField annotationField : request.getFilter().getFields()) {
264  0 String filterName = annotationField.getName();
265  0 List<String> values = filters.get(filterName);
266  0 if (values == null) {
267  0 values = new ArrayList<String>();
268  0 filters.put(filterName, values);
269    }
270  0 if (annotationField.getValue() != null) {
271  0 values.add(annotationField.getValue());
272    }
273    }
274   
275  8 if (filters.size() == 0) {
276  8 return annotations;
277    }
278   
279  0 for (Annotation ann : annotations) {
280  0 boolean matches = true;
281  0 for (Map.Entry<String, List<String>> filter : filters.entrySet()) {
282  0 Object annotationValue = ann.get(filter.getKey());
283    // if the values is not set or is not among the requested values,
284  0 if (annotationValue == null || !filter.getValue().contains(annotationValue.toString())) {
285    // it doesn't match and exit
286  0 matches = false;
287  0 break;
288    }
289    }
290    // if it matches in the end, add it to the results
291  0 if (matches) {
292  0 result.add(ann);
293    }
294    }
295  0 return result;
296    }
297   
298    /**
299    * @return the xwiki user in the context.
300    */
 
301  12 toggle protected String getXWikiUser()
302    {
303  12 return this.xcontextProvider.get().getUser();
304    }
305   
306    /**
307    * Helper method to make sure that the context is set to the right document and database name.
308    *
309    * @param wiki the REST wikiName path parameter
310    * @param space the REST spaceName path parameter
311    * @param page the REST pageName path parameter
312    */
 
313  8 toggle protected void updateContext(DocumentReference documentReference)
314    {
315  8 try {
316    // Set the database to the current wiki.
317  8 XWikiContext deprecatedContext = (XWikiContext) execution.getContext().getProperty("xwikicontext");
318  8 deprecatedContext.setWikiId(documentReference.getWikiReference().getName());
319   
320    // Set the document to the current document.
321  8 XWiki xwiki = deprecatedContext.getWiki();
322  8 XWikiDocument currentDocument =
323    xwiki.getDocument(documentReference, deprecatedContext);
324  8 deprecatedContext.setDoc(currentDocument);
325    } catch (Exception e) {
326    // Just log it.
327  0 logger.log(Level.SEVERE,
328    String.format("Failed to update the context for page [%s].", documentReference), e);
329    }
330    }
331    }