Custom format and location of annotation storage

I mean others. When two users annotate the same document, they both see all annotations of other users. Is it possible that the annotations of one user are “private” and unvisible for all other users?

Hello, Alexey.


Thank you for this suggestion and code example. Our developers already got a note about it. We will inform you soon more details about this functionality implementation. Please, stay tuned.
Hello Alexey,

Unfortunately right now there is no functionality to store annotations separately or provide them privately out-of-the-box.

You can try to use the AnnotationHandler.listAnnotations(String fileGuid) and in the listAnnotationsHandler request return annotations filtered for each user. To do this – examine the AJAX request from the front-end in your browser developer tools. There you will see what data is sent to the server and what if the response format. Document guid (as fileId) and user guid (as userId) are both in that request. You can use these values to pick up only necessary annotations and form a response using them.

If you want some independent storage functionality or extended user access rights we can put it into our roadmap, but priorities of all requested features we develop depend on the license type that the customer bought.

hi,

we have the following scenario, In database there is a docx document . We create a folder in the file system and copy the document in this folder. Now you can annotiren the document. Once with annotation is completed, all xml’s with annotation’ info be stored in database, the folder is deleted. Now we have a problem with the fact that, in your xml’s (for example in AnnDocs.xml as documentGuid) is hard-coded the name of the folder. of course you have to consider the case that the annotated document could be annotated later again. We would have liked the option of defining the name of the folder itself
Hallo Ihor, Thaks for your answer,

your solution does not suit to me. I have follow idea: I am going to start for each annotation process a separatly groupdocs server. But I have a problem: it does not work. When I start a new instance on other port I see the Exception:

WARN [2014-11-25 08:43:17,833] org.eclipse.jetty.util.component.AbstractLifeCycle: FAILED SocketConnector@0.0.0.0:8081: java.net.BindException: Address already in use: JVM_Bind! java.net.BindException: Address already in use: JVM_Bind

i write in configuration for first server :

applicationPath: http://127.0.0.1:8080/
and a new line:
serverPort: 8080
and in MainApplication in "run" method:
applicationConfig.getHttpConfiguration().setPort(applicationConfig.getServerPort());

and for second server:
applicationPath: http://127.0.0.1:8090/
and a new line:
serverPort: 8090

Theoretical it must work because the ports are different. But i see in Exception the port 8081. Have you hardcoded the port nummer ?

How can I resolve the problem ?

Thanks im advance

Alexey
Hello Alexey.
The functionality of manual GUID/Name manipulation you request is dependent on the underling GroupDocs.Viewer library and has to be implemented there first.
Unfortunately, at the moment GroupDocs.Viewer developers are very busy. They are going to release a new version this week with a bunch of tweaks and have a lot of work to do. We will review the ability to manually manage IDs/names from the next week. We’ve already put your feature-request on our roadmap. We could already implement it but feature priorities in our roadmap queue depend on the demand and the customer’s license type who requests them.
At the moment we suggest you next.
alexeyschroeder:
but i can't load replays because the method List loadData() doesn't have the information about the document,it means i dont know where is the document?
For now you can try to obtain the AnnDoc object. It contains the document id.
DaoFactory.createAnnDocDao().selectBy(asList(IAnnDoc.ANNOTATION), annotation.getId()).getDocumentId()
Also, I see you are storing the annotation metadata in the database. You can look how the GroupDocs.Annotation stores it. If you are interested, here are DB configuration settings: from line 31 to line 54.
What about the GroupDocs.Annotation progress. On your request we implemented very flexible meta-data storage API in the version that is going to be released on October 31. It provides several options to store annotations, replies, users, etc.:
  • ONE – all data is stored in a single file
  • FEW – all data is stored by categories in 7 files (annotations, users, replies, etc.)
  • MANY – same as FEW, but annotations are stored per document within the document temporary (cache) folder, together with generated page images
  • LOT – same as MANY, but annotations additionally are stored in separate files per user who created them (file name is equal to the user ID)
Furthermore, you have a complete ability to override this functionality and implement your own one.

Hello Alexey,


Ports configuration has nothing to do with the GroupDocs.Annotation for Java library.
In the Dropwizard sample you can configure server and admin ports using next configuration settings:

#Server configuration
#Set available ports for application to use
http:
port: 8090
adminPort: 8091

It should resolve the “address already in use” error.

Hi,

I don’t understand the code:

DaoFactory.createAnnDocDao().selectBy(asList(IAnnDoc.ANNOTATION), annotation.getId()).getDocumentId()

I don’t have the link to object “annotation”. I have nothing in this moment(when i am in method " loadData()"). Or I missed something?
Hello Alexey,

I meant that you can use this code on the saving stage to find out where the meta-data is stored.

In the loadData method there is no need to have a link to the “annotation” object, there you just need to read all meta-data and return it up from the method. It will be “linked” further using corresponding IDs and GUIDs.

We use a specific algorithm, so there should not be the folder name (GUID) collisions. But we are not sure if the GUID will be the same after the file (folder with a file) was removed and recreated.

I think, you can try to implement next algorithm, then you do not have to bother about GUIDs at all.
  1. Create a file (in the basePath) using data in your DB
  2. Get the file’s GUID using Utils.encodeData(String) and the file full path as a parameter
  3. Look into you DB for the meta-data (in case it’s not the first opening of the file) and load it into the GroupDocs.Annotation cache folder using obtained GUID
    Note: at this stage you should already know where actually the meta-data file would be stored, because you choose the save implementation manually or implement it by yourself
  4. Run the GroupDocs.Annotation
  5. Let users process the document
  6. Close the GroupDocs.Annotation
  7. Copy updated meta-data into your DB
  8. Remove data from the GroupDocs.Annotation cache
  9. Remove the document from the basePath
So, the key point here is obtaining the GUID through the Utils.encodeData(String) method.

Hi,
excuse me, i don't understand again. How can I know, which document I must load? I have many documents that are annotated at the moment(just now). I must have someone information for the document which are annotated.

Situation: 20 documents are annotated at the moment. The method "loadData()" is called. The question: which document must I take?
Hi Alex,

Let me explain. The idea is to load everything in the loadData() method what is available, but you decide what is available.
Using methods
Utils.encodeData(String filePath)
Utils.decodeData(String fileGUID)
you is able to figure out where to paste data from the DB and what data to copy back into the DB.

alexeyschroeder:
Situation: 20 documents are annotated at the moment. The method "loadData()" is called. The question: which document must I take?
The situation should start before the loadData() method call. Together with supplying these 20 documents you have to place in correct locations all available in the DB meta-data (here is where encode/decode methods are in help). The loadData() should load all meta-data then the GroupDocs.Annotation will pick-up correct annotations for opened files.

Hi,
do you mean, if there are 20 documents, than I must load all 20 XMLs for f.e. replys?
it is not performant.

if it is so, I see a problem with simultaneous access to a xml - files. For example User A annotates a document, at moment is his xml writted. User B want to see another document simultaneous. I try to load xml for first document and get exception because his xml not exist(is writted in moment).


Hi Alexey,

Sorry for so long delay, I had to completely investigate the situation and make some debugging.
Let’s start from that the classes code you can find on the github is a sample implementation. It’s not perfect but a demonstration of the functionality. Its logic is next. When some annotation has to be processed it uses the loadData() method, that we discussed, to obtain all annotations and then in the loop select the needed one. Why it’s developed in this way I’ll explain below. The main thing is that this approach is an example. For your case, probably, you have to implement another approach, where part of the current one may be used.

To implement custom meta-data save/load logic you have to implement the IDataConector and all I*Dao (IUserDao, ISessionDao …) interfaces. Here is the class diagram for these interfaces: http://screencast.com. Then the AnnotationHandler has to be initialized with the implemented IDataConector object, through:
AnnotationHandler(com.groupdocs.viewer.config.ServiceConfiguration config, com.groupdocs.annotation.data.connector.IConnector connector)
When working with entities (annotations, replies, users, documents …) the GroupDocs.Annotation uses implemented I*Dao (IUserDao, ISessionDao …) interfaces and their selectBy(…) and selectByAll(…) methods to manage (load, edit, delete) objects.
Semantics of these methods arguments is next. The first argument is the list of parameter names, all other arguments are the parameter values. For example when some annotation is edited the GroupDocs.Annotation calls
IAnnotationDao.selectBy(List(“AnnotationGuid”), annotationGuid)
, similarly all annotations are asked with the session id:
IAnnotationDao.selectByAll(List(“AnnotationSessionId”), annotationSessionId)
In most cases the selectBy method will be called with the annotation guid parameter, and the selectByAll – session id, but in general there may be any number of different parameters. That’s why our implementation (CustomAbstractDaoImpl) loads all data and then selects necessary items in the loop.

As I understood, you want to open only the file with annotations for the requested document. In this case you can implement the selectBy and selectByAll methods by yourself. Here I’ll provide you some tips how to get necessary data.
  • Get the document GUID using the session id (selectByAll)
    ISession sess = DaoFactory.createSessionDao().selectBy(Arrays.asList(ISession.ID), sessionId);
    String docGUID = DaoFactory.createDocumentDao().selectBy(Arrays.asList(IDocument.ID), sess.getDocumentId()).getDocumentName();
  • Get the document GUID using the annotation guid (selectBy)
    DaoFactory.createAnnDocDao().selectBy(Arrays.asList(IAnnDoc.ANNOTATION_GUID), annotationId).getDocumentGuid()
    or you can obtain it through the session entity, similarly to previous way.
Now you can add few lines of code here to directly open the necessary file and load meta-data.

I encourage you to add some check if the arguments are indeed the session id or the annotation guid. Because if they are not – then it will be better to implement the general approach – load all available data and select in the loop or somehow another handle this case. Don’t worry this code may never run, but it will be better to have it.

To handle the concurrent access problems, I think, you will have to implement some blocking (mutex) technique to access documents in a queue. This have to be implemented anyway, is it a file access or a DB management.

P.S. Another option is to use our SQLite storage option and store everything into single SQLite file. Then you can open it and select any info that is interested for you. It may be easier there, because of the relations visible more clearly.

Hi Ihor,
thank you for your detailed answer.

I have a small problem with type "user". In method selectBy(final List fieldNames, final Object... fieldValues) for user there is only one parameter "userName" in list "fieldNames". It is not enough to find out which document is annotated in moment. I can of course load all users, but it is very bad idea.
Could you do so, that the parameter "documentGuid" or "documentId" there is in "fieldNames" too?
Thanks in advance

Hi Alexey, you are welcome.


Let my clarify. Do you also want to store users separately per each document? That’s why you need some pointer to the document?

yes, you understand correctly

Ok, Alexey, I have notified our developers about your request. They will review it as soon as possible. Then we will reply here with news. Stay tuned, please.