Noob Issue - UUID - Issues trying to desearlize in URL

I have been working on this for awhile and I can’t seem to figure it out.

I have routes working correctly but the issue is when I try to pass the UUID in the URL. There seems to be an issue when I try to convert the UUID (id pk) to a serialized value in the URL.

image

Here is my my document edit class -

import com.company.janice.entity.ExtUser;
import com.company.janice.entity.YesNoEnum;
import com.google.common.collect.ImmutableMap;
import com.haulmont.cuba.core.global.DataManager;
import com.haulmont.cuba.gui.Notifications;
import com.haulmont.cuba.gui.Route;
import com.haulmont.cuba.gui.UrlRouting;
import com.haulmont.cuba.gui.components.DateField;
import com.haulmont.cuba.gui.components.HasValue;
import com.haulmont.cuba.gui.components.LookupField;
import com.haulmont.cuba.gui.components.TextField;
import com.haulmont.cuba.gui.model.InstanceContainer;
import com.haulmont.cuba.gui.navigation.UrlParamsChangedEvent;
import com.haulmont.cuba.gui.screen.*;
import com.company.janice.entity.Document;
import com.haulmont.cuba.security.global.UserSession;
import com.haulmont.cuba.web.sys.navigation.UrlIdSerializer;

import javax.inject.Inject;
import java.util.Date;
import java.util.UUID;

@Route(value = "doc/edit", parentPrefix = "doc")
@UiController("janice_Document.edit")
@UiDescriptor("document-edit.xml")
@EditedEntityContainer("documentDc")
@LoadDataBeforeShow
public class DocumentEdit extends StandardEditor<Document> {
    @Inject
    private UserSession userSession;
    @Inject
    private InstanceContainer<Document> documentDc;
    @Inject
    private LookupField<YesNoEnum> additionalReviewRequiredField;
    @Inject
    private DateField<Date> draftSentToCustomerField;
    @Inject
    private TextField<String> customerReviewCompletedField;
    @Inject
    private DateField<Date> customerReviewDueField;
    @Inject
    private DateField<Date> customerReviewCompletedDateField;
    @Inject
    private LookupField<YesNoEnum> secondAdditionalReviewRequiredField;

    @Inject
    private DateField<Date> relevantTeamMembersNotifiedField;
    @Inject
    private DateField<Date> finalizedDocumentFiledInCollaborativeWorkspaceField;
    @Inject
    private TextField<String> customerApprovalReceivedField;
    @Inject
    private DateField<Date> customerApprovalReceivedDateField;
    @Inject
    private DateField<Date> dateFinalizedField;
    @Inject
    private DateField<Date> signauresOfAllReviewersField;
    @Inject
    private DateField<Date> finalSentForInternalApprovalField;
    @Inject
    private DateField<Date> annualReviewField;

    @Inject
    private UrlRouting urlRouting;
    @Inject
    private DataManager dataManager;
    @Inject
    private Notifications notifications;

    @Subscribe
    private void onInitEntity(InitEntityEvent<Document> event) {

        Document document = event.getEntity();
        if (document == null) {
            urlRouting.replaceState(this);
            return;
        }
        String serializedDocumentId = UrlIdSerializer.serializeId(document.getId());
        urlRouting.replaceState(this, ImmutableMap.of("id", serializedDocumentId));

        //Get currently signed in user and assign to approrpiate field.
        event.getEntity().setAuthorName((ExtUser) userSession.getCurrentOrSubstitutedUser());

        //Hide Elements
        draftSentToCustomerField.setVisible(false);
        customerReviewCompletedField.setVisible(false);
        customerReviewDueField.setVisible(false);
        customerReviewCompletedDateField.setVisible(false);
        secondAdditionalReviewRequiredField.setVisible(false);

        finalizedDocumentFiledInCollaborativeWorkspaceField.setVisible(false);
        customerApprovalReceivedField.setVisible(false);
        customerApprovalReceivedDateField.setVisible(false);
        dateFinalizedField.setVisible(false);
        signauresOfAllReviewersField.setVisible(false);
        finalSentForInternalApprovalField.setVisible(false);
        relevantTeamMembersNotifiedField.setVisible(false);
        annualReviewField.setVisible(false);



    }



    @Subscribe("additionalReviewRequiredField")
    private void onAdditionalReviewRequiredFieldValueChangeListener(HasValue.ValueChangeEvent<YesNoEnum> event){
        Enum<YesNoEnum> reviewRequired = documentDc.getItem().getAdditionalReviewRequired();

        if (reviewRequired == YesNoEnum.NO) {
            assembleCustomerReview();
        }
        if (reviewRequired == YesNoEnum.YES) {
            hide();
        }
    }


    @Subscribe("secondAdditionalReviewRequiredField")
    private void onSecondAdditionalReviewRequiredFieldValueChangeListener(HasValue.ValueChangeEvent<YesNoEnum> event){
        Enum<YesNoEnum> reviewRequired = documentDc.getItem().getSecondAdditionalReviewRequired();

        if (reviewRequired == YesNoEnum.NO) {
            assembleDocumentFinalization();
        }
        if (reviewRequired == YesNoEnum.YES) {

        }
    }


    private void assembleCustomerReview() {
        draftSentToCustomerField.setVisible(true);
        customerReviewCompletedField.setVisible(true);
        customerReviewDueField.setVisible(true);
        customerReviewCompletedDateField.setVisible(true);
        secondAdditionalReviewRequiredField.setVisible(true);
    }

    private void assembleDocumentFinalization() {
        finalizedDocumentFiledInCollaborativeWorkspaceField.setVisible(true);
        customerApprovalReceivedField.setVisible(true);
        customerApprovalReceivedDateField.setVisible(true);
        dateFinalizedField.setVisible(true);
        signauresOfAllReviewersField.setVisible(true);
        finalSentForInternalApprovalField.setVisible(true);
        relevantTeamMembersNotifiedField.setVisible(true);
        annualReviewField.setVisible(false);

    }


    private void hide() {
        draftSentToCustomerField.setVisible(false);
        customerReviewCompletedField.setVisible(false);
        customerReviewDueField.setVisible(false);
        customerReviewCompletedDateField.setVisible(false);
        secondAdditionalReviewRequiredField.setVisible(false);

        finalizedDocumentFiledInCollaborativeWorkspaceField.setVisible(false);
        customerApprovalReceivedField.setVisible(false);
        customerApprovalReceivedDateField.setVisible(false);
        dateFinalizedField.setVisible(false);
        signauresOfAllReviewersField.setVisible(false);
        finalSentForInternalApprovalField.setVisible(false);
        relevantTeamMembersNotifiedField.setVisible(false);
        annualReviewField.setVisible(false);
    }


   @Subscribe
    public void onUrlParamsChanged(UrlParamsChangedEvent event) {
        String serializedDocumentId = event.getParams().get("id");
        UUID documentId = (UUID) UrlIdSerializer.deserializeId(UUID.class, serializedDocumentId);
        Document editDocument = dataManager.load(Document.class).id(documentId).one();
        setEntityToEdit(editDocument);
    }



}

Any help or suggestions as to what I am doing wrong would be appreciated.

Hi @Stephen,
I think that you can delete all the logic related to routing you implemented in your controller. Since you are using a StandardEditor and your parameter is the entity ID (UUID), only the @Route annotation is required.

Regards,
Peterson.

Hi @Stephen,

I agree with @peterson.br, your custom logic seems unnecessary. Also, pay attention that InitEntityEvent is fired before the new entity instance is set to edited entity container, i.e. for an entity that is not committed to DB. In this case, the new keyword is used instead of serialized entity id, e.g. doc/edit/new.

Regards,
Gleb

Thank you @peterson.br and @gorelov -

I removed the code and routes work as expected! -

I guess I need to rephrase my original question.
Is it possible to redirect a URL that is passed with it’s UUID ID to the serialized one?

Meaning if I enter this into the URL - (where id= is the actual PK UUID from the DB)
http://localhost:8080/docmanager/#main/1/doc/edit?id=9271124a-c0a9-e373-9c44-20b9549a67e1

The URL will then redirect to the searlized version of the URL
http://localhost:8080/docmanager/#main/1/doc/edit?id=4je494ng59wdssrh10q5a9msz1

I appreciate any additional insights either of you can add.

For such requirements, I would recommend creating a custom NavigationHandler in the Web module, e.g.:

import com.haulmont.cuba.gui.navigation.NavigationState;
import com.haulmont.cuba.web.AppUI;
import com.haulmont.cuba.web.sys.navigation.UrlChangeHandler;
import com.haulmont.cuba.web.sys.navigation.UrlIdSerializer;
import com.haulmont.cuba.web.sys.navigation.navigationhandler.NavigationHandler;
import org.apache.commons.collections4.MapUtils;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.Optional;
import java.util.UUID;

@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
@Order(10)
public class CustomScreenNavigationHandler implements NavigationHandler {

    @Override
    public boolean doHandle(NavigationState requestedState, AppUI ui) {
        Map<String, String> params = requestedState.getParams();
        // If params contains an Id
        if (MapUtils.isNotEmpty(params)
                && params.containsKey("id")) {
            String id = params.get("id");

            try {
                // and Id can be converted to UUID
                UUID uuid = UUID.fromString(id);
                // we replace it with serialized value
                params.put("id", UrlIdSerializer.serializeId(uuid));

                // redirect to the correct URL
                UrlChangeHandler urlChangeHandler = ui.getUrlChangeHandler();
                urlChangeHandler.redirect(requestedState);

                // and inform that no further handling is needed
                return true;
            } catch (IllegalArgumentException ignored) {
            }
        }

        return false;
    }
}

Regards,
Gleb

Hey Gleb,
.
I had the same issue, so I implemented your custom navigation handler method, but it seems to make me log in to the application again, even when I’m already logged in. It seems to work after that, but it’s strange. Any idea of why this is happening and of a fix for this behaviour?

Thanks,
Adam

Hi,

Could you please attach a demo project that demonstrates the issue, so I can analyze it and debug?

Regards,
Gleb

navigation.zip (89.2 KB)

This is a test project I created that functions the same way.

Thanks,
Adam

Hi,

Thank you for the demo project, below the improved version of the navigation handler:

@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
@Order(10)
public class CustomScreenNavigationHandler extends ScreenNavigationHandler {
    @Override
    public boolean doHandle(NavigationState requestedState, AppUI ui) {
        Map<String, String> params = requestedState.getParams();
        // If params contain an Id
        if (MapUtils.isNotEmpty(params)
                && params.containsKey("id")) {
            String id = params.get("id");

            try {
                // and Id can be converted to UUID
                UUID uuid = UUID.fromString(id);
                // we replace it with serialized value
                params.put("id", UrlIdSerializer.serializeId(uuid));
            } catch (IllegalArgumentException ignored) {
            }
        }

        // and invoke super for further processing
        return super.doHandle(requestedState, ui);
    }
}

Two main difference:

  1. Extend built-in ScreenNavigationHandler
  2. Instead of redirecting, invoke doHandle from ScreenNavigationHandler

Gleb

1 Like

Thanks Gleb. It works!