How to implement a custom document handler?

Hi,


I want to implement a custom document handler. That means reading the document not from a file system, but from a content reposiory such as Alfresco.
I see that AnnotationHandler.getAnnotationScript method accepts the file path only which means I need to download the file to some local cache first and then pass that path. Is that true? Is there any better way to handle this? If not, is there some built in mechanism to clean up that local cache?

Thanks in advance for any help,
Mariusz

I tried doing code below, but when I open an URL with ?objectId=… it doesn’t even invoke the getFile method from the custom handler. The UI says Loading content and nothing is loading. Only getFileList method is invoked from the handler, but that’s it. Any clue how to make it work?


1. HomeController:

@RequestMapping(value = “/”, method = RequestMethod.GET)
public String index(Model model, HttpServletRequest request, HttpServletResponse response, @RequestParam(value = “objectId”,
required = true) String objectId, @RequestParam(value = “userName”, required = false) final String userName) throws Exception {
// Setting header in jsp page
try {
model.addAttribute(“groupdocsHeader”, annotationHandler().getHeader(applicationConfig.getApplicationPath(), request));
final String userGuid = annotationHandler().addCollaborator(userName, objectId,
AccessRights.from(AccessRights.CAN_VIEW, AccessRights.CAN_ANNOTATE), Utils.colorToInt(Color.black));
model.addAttribute(“groupdocsScripts”, annotationHandler().getAnnotationScript(objectId, userName, userGuid));
} catch (Throwable e) {
e.printStackTrace();
}
return “index”;
}

2. annotationHandler() method - initialization:

final IConnector connector = new CustomXmlDataConnector();
annotationHandler = new AnnotationHandler(serviceConfiguration, new CustomInputDataHandler(serviceConfiguration), connector);

3. CustomInputDataHandler:
public class CustomInputDataHandler extends InputDataHandler {
public CustomInputDataHandler(ServiceConfiguration serviceConfiguration) {
}

@Override
public InputStream getFile(String guid) {
System.out.println("Object ID: " + guid);
InputStream result = null;
//app logic to get the file by ID
return null;
}

@Override
public File[] getFileList(String directory) {
return new File[] {};
}

@Override
public FileType getFileType(String guid) {
return FileType.DOCX;
}

@Override
public String saveFile(InputStream inputStream, String fileName, Integer timeToLive) {
throw new UnsupportedOperationException(“Not supported.”);
}
}



Thanks,
Mariusz
Hello Mariusz,

There is an API provided for creating a custom input data source. I encourage you to check the documentation on the custom Input DataSource. Also, as an example you can refer to our implementation of the Alfresco CMIS InputDataHandler, I’m attaching it to this post.

The issues you have found earlier (filed as VIEWERJAVA-551) have been fixed in this update.


This message was posted using Notification2Forum from Downloads module by groupdocs.notifier.
(1)

As you can see in the above example I did it in the same way. But file is not loading and there is no exception in the log. getFile method is not even invoked.


Any idea what is wrong?

I’m attaching a Fiddler log from the request that returns success=false although the ID is passed correctly and there is no exception in the logs.

Thanks,
Mariusz

I was able only to find where the exception is caught. Unfortunately you catch it and there is no way to find our what is is exactly. See the attached screenshots.


java.lang.NullPointerException
at com.groupdocs.viewer.b.b.(DocumentToImageConverter.java:37)
at com.groupdocs.viewer.handlers.ViewerHandler.viewDocument(ViewerHandler.java:543)
at com.groupdocs.viewer.handlers.ViewerHandler.viewDocumentHandler(ViewerHandler.java:483)
at com.groupdocs.annotation.handler.AnnotationHandler.viewDocumentHandler(AnnotationHandler.java:500)
at com.groupdocs.spring.slim.HomeController.viewDocumentHandler(HomeController.java:588)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:219)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:745)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:686)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:936)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:838)

Any update on this?

It really makes the integration impossible.

I tried two approaches concerning the entry point. Tried passing the object ID directly and via the EncodedPath:


1. String initialPath = objectId;
2. GroupDocsPath path = new EncodedPath(objectId, annotationHandler().getConfiguration());
String initialPath = path.getPath();


And the the rest:
String script = annotationHandler().getAnnotationScript(initialPath, userName, userGuid);
model.addAttribute(“groupdocsScripts”, script);


Then the annotationHandler() method looks as follows:
protected AnnotationHandler annotationHandler() {
if (annotationHandler == null) {
final ServiceConfiguration serviceConfiguration = new ServiceConfiguration(applicationConfig);
try {
//final IConnector connector = new CustomXmlDataConnector();
annotationHandler = new AnnotationHandler(serviceConfiguration, new CustomInputDataHandler(serviceConfiguration));
InputDataHandler.setInputDataHandler(new CustomInputDataHandler(serviceConfiguration));
} catch (final Exception e) {
// TODO: logger
e.printStackTrace();
}
}
return annotationHandler;
}


No exception visible, but the CustomInputDataHandler.getFile method is never called.
Can you help solving that issue?

Thanks in advance,
Mariusz Pala
Hello Mariusz, sorry for so long delay with the response.

We’ve investigated this issue and its roots. It appeared that this issue was meant to be fixed in the 2.5.0 release. We are going to release the GroupDocs.Viewer 2.6.0 next week where this issue is fixed. If you want we can prepare a beta version of the GroupDocs.Annotation for you which will contain this new Viewer version.

Currently, as a temporary solution you can fix it in the next way:
From the getFileList method return File array containing only single file – the file you want to open. The same time you should encode filename of that file with com.groupdocs.viewer.utils.Utils.encodeData and pass the obtained value to the UI (through addCollaborator and getAnnotationScript methods of the AnnotationHandler instance).

OK. In the getFileList method I cannot return a file as I don’t have any. The file should be streamed from some other system via getFile method. Thus for the purpose of that method I’d have to additionally download it somewhere which makes no sense.


How should the entry point look like? Assuming I have two parameters: objectId and userName, which code should I use?

1.
objectId = com.groupdocs.viewer.utils.Utils.encodeData(objectId);
final String userGuid = annotationHandler().addCollaborator(usedUserName, objectId, AccessRights.All,
Utils.colorToInt(Color.black));
String script = annotationHandler().getAnnotationScript(objectId, userName, userGuid);
model.addAttribute(“groupdocsScripts”, script);

2.
GroupDocsPath path = new EncodedPath(objectId, annotationHandler().getConfiguration());
objectId = path.getPath();

final String userGuid = annotationHandler().addCollaborator(usedUserName, objectId, AccessRights.All,
Utils.colorToInt(Color.black));
String script = annotationHandler().getAnnotationScript(objectId, userName, userGuid);
model.addAttribute(“groupdocsScripts”, script);


Concerning the annotation handler initialization, I guess that’s the correct syntax, can you confirm?

annotationHandler = new AnnotationHandler(serviceConfiguration, new CustomInputDataHandler(serviceConfiguration));

Thank you for your help,
Mariusz

I’d love to get some debug version, but it would have to be groupdocs.annotation maven version referencing that new viewer version. So I can wait for v1.8. I’m waiting to get some connector info from your side anyway, so there is no rush in releasing it right away. Thanks.

Hello Mariusz,

Better is to use the second code example. It’s more properly to use EncodedPath, using of the Utils is more like a hot-fix.

Initialization is ok, also you can use the InputDatahandler static method, which also works ok:
InputDataHandler.setInputDataHandler(new CustomInputDataHandler(serviceConfiguration));

Currently without providing correct File[] list from the getFileList the getFile method will not be executed as you figured it out earlier. So I think the best way will be to wait for the 1.8.0 release of the GroupDocs.Annotation for Java library with the referenced GroupDocs.Viewer of the 2.6.0 version.

Hi,


right now InputDataHandler contains a static reference to the data handler and it should not be static!
Since our CustomDataInputHandler is user sepecific and thus HTTP session specific, we need to store it as the HTTP session attribute. We store the AnnotationHandler like that already. Unfortunately the AnnotationHandler despite of that it’s one per user session, it references the same CustomDataInputHandler instance as it’s static. See e.g. the ViewerHandler constructor.

That’s unfortunately a showstopper for us.
Please let me know if you plan to change it.

Thanks,
Mariusz
Hello Mariusz,

The implementation of the InputDataHandler processing is planned to be changed as you requested. It should be released with the bug fix in the 2.6.0 version of the GroupDocs.Viewer. You’ll get informed about the release.