Issue Details (XML | Word | Printable)

Key: GLASSFISH-18861
Type: Bug Bug
Status: Resolved Resolved
Resolution: Fixed
Priority: Critical Critical
Assignee: Amy Roh
Reporter: Jeremy_Lv
Votes: 0
Watchers: 2
Operations

If you were logged in you would be able to see more operations.
glassfish

After setting context root of two wars which deployed on the server target with the same value,both of the two wars accessed failed.

Created: 03/Jul/12 06:13 AM   Updated: 12/Mar/13 04:16 PM   Resolved: 12/Mar/13 04:16 PM
Component/s: web_container
Affects Version/s: 4.0_b41
Fix Version/s: 4.0_b80_EE7MS6

Time Tracking:
Not Specified

File Attachments: 1. File test_sample1.war (3 kB) 03/Jul/12 06:13 AM - Jeremy_Lv
2. File test_sample2.war (3 kB) 03/Jul/12 06:13 AM - Jeremy_Lv

Environment:

Windows Xp


Tags:
Participants: Amy Roh, Hong Zhang, Jeremy_Lv, Shing Wai Chan and Tom Mueller


 Description  « Hide

[Bug Description]
After setting context root of two wars which deployed on the server target with the same value,both of the two wars accessed failed.

[Operations]
1Deploy two wars called test_sample1.war and test_sample2.war to the server target and ensure that the two wars can be accessed.
(asadmin deploy test_sample1.war)
(asadmin deploy test_sample2.war)

2.From admin gui to edit the configuration of test_sample2.war and change the context root's value of test_sample2.war into "/test_sample1",and then the context root's value of test_sample2.war is the same as test_sample1.war successfully.

Note: when looking server.log, the following error messages appeared:

[#|2012-07-03T13:19:16.984+0800|WARNING|44.0|javax.enterprise.system.container.web.com.sun.enterprise.web|_ThreadID=73;_ThreadName=Thread-2;|java.lang.Exception: WEB0113: Virtual server [server] already has a web module [test_sample1] loaded at [/test_sample1]; therefore web module [test_sample2] cannot be loaded at this context path on this virtual server.
java.lang.Exception: WEB0113: Virtual server [server] already has a web module [test_sample1] loaded at [/test_sample1]; therefore web module [test_sample2] cannot be loaded at this context path on this virtual server.

3.When you accessed "http://localhost:8080/test_sample1",the error message of "HTTP Status 404 - Not Found" happened.

4.The more serious problem is that,from admin gui,the modified war(test_sample2) can't be undeployed and there is no any useful message to tell user why the war can't be undeployed.

Note: when looking server.log again, the following stacktrace appeared:
[#|2012-07-03T13:22:40.968+0800|SEVERE|44.0|javax.enterprise.system.tools.admin.org.glassfish.deployment.admin|_ThreadID=69;_ThreadName=Thread-2;|Application not registered|#]

[#|2012-07-03T13:22:40.968+0800|INFO|44.0|javax.enterprise.system.tools.admin.org.glassfish.deployment.admin|_ThreadID=69;_ThreadName=Thread-2;|PostUndeployCommand starting|#]

[#|2012-07-03T13:22:40.968+0800|INFO|44.0|javax.enterprise.system.tools.admin.org.glassfish.deployment.admin|_ThreadID=69;_ThreadName=Thread-2;|PostUndeployCommand done successfully|#]

[Bug Level Analyse]
There is the following use case that shows the problem will not be acceptable by user.

UseCase: if user has deployed an large economic application on GF Server, this economic application can work effectively until another
application is deployed and some administrator changed the latter application's context root's value with the economic application's context root's value.
Then the economic application can't work any longer!

[Affected versions ]
1 4.0_b41
2 gf's trunk until 2012/06/22



Hong Zhang added a comment - 03/Jul/12 02:19 PM - edited

I was able to reproduce the problem, that the test_sample1 is no longer available at the context root after test_sample2 failed to get reloaded with the conflicted context root (I was actually able to undeploy test_sample2 after the failed reloading, maybe you were trying to undeploy multiple times after the failed reloading?). I will discuss with web team on this to see why the test_sample1 was also affected by the failed reloading of test_sample2.


Jeremy_Lv added a comment - 04/Jul/12 07:56 AM

I reporduce my two wars again and ask my co-workers to reproduce it too. The test_sample2 can't be undeployed after the test_sample2 failed to get reloaded with the conflicted context root.(I have tried it for multiple times)


Jeremy_Lv added a comment - 04/Jul/12 09:40 AM - edited

I have created another trac related to this issue about the deployment:
the trac as follows:
http://java.net/jira/browse/GLASSFISH-18866


Shing Wai Chan added a comment - 26/Jul/12 08:02 PM - edited

After fixing 18866, I notice a different behavior. The test_sample1 is working, but test_sample2 is not.
And while setting the context root of test_sample2 to be /test_sample1. I notice that
WebContainer#loadWebModule has an exception for duplicateContextRoot. And ApplicationLifecycle#deploy execute the following code:

} catch(Throwable loadException) {
        logger.log(Level.SEVERE, loadException.getMessage(), loadException);
        report.failure(logger, "Exception while loading the app", null);
        report.setFailureCause(loadException);
        tracker.actOn(logger);
        return null;
    }

And the command is returned as successful in admin gui - "New values successfully saved"


Jeremy_Lv added a comment - 27/Sep/12 03:20 AM

Hong:
I have some ideas about this issue.
The reason why this bug comes out is because the duplicated context setting into domain.xml without any validation.
The logical about incompletely setting is as follows:


1)SetCommand#execute(start setting changes in this method)

if (!set(context, value))
                return;

2)SetCommand#set(put the changed attributes into the map called "changes" and call the code in HK2 to set the changes to domain.xml)
1>

Map<ConfigBean, Map<String, String>> changes = new HashMap<ConfigBean, Map<String, String>>();

        boolean setElementSuccess = false;
        boolean delPropertySuccess = false;
        boolean delProperty = false;
        Map<String, String> attrChanges = new HashMap<String, String>();
        if (isProperty) {
            attrName = "value";
            if ((value == null) || (value.length() == 0)) {
                delProperty = true;
            }
            attrChanges.put(attrName, value);
        }

        List<Map.Entry> mNodes = new ArrayList(matchingNodes.entrySet());
        if (applyOverrideRules) {
            mNodes = applyOverrideRules(mNodes);
        }
        for (Map.Entry<Dom, String> node : mNodes) {
            final Dom targetNode = node.getKey();

            for (String name : targetNode.model.getAttributeNames()) {
                String finalDottedName = node.getValue() + "." + name;
                if (matches(finalDottedName, pattern)) {
                    if (attrName.equals(name) ||
                            attrName.replace('_', '-').equals(name.replace('_', '-')))  {
                        if (isDeprecatedAttr(targetNode, name)) {
                           warning(context, localStrings.getLocalString("admin.set.deprecated",
                                   "Warning: The attribute {0} is deprecated.", finalDottedName));
                        }

                        if (!isProperty) {
                            targetName = prefix + finalDottedName;

                            if (value != null && value.length() > 0) {
                                attrChanges.put(name, value);
                            } else {
                                attrChanges.put(name, null);
                            }
                        } else {
                            targetName = prefix + node.getValue();
                        }

                        if (delProperty) {
                            // delete property element
                            String str = node.getValue();
                            if (trueLastIndexOf(str, '.') != -1) {
                                str = str.substring(trueLastIndexOf(str, '.') + 1);
                            }
                            try {
                                if (str != null) {
                                    ConfigSupport.deleteChild((ConfigBean) targetNode.parent(), (ConfigBean) targetNode);
                                    delPropertySuccess = true;
                                }
                            } catch (IllegalArgumentException ie) {
                                fail(context, localStrings.getLocalString("admin.set.delete.property.failure", "Could not delete the property: {0}",
                                        ie.getMessage()), ie);
                                return false;
                            } catch (TransactionFailure transactionFailure) {
                                fail(context, localStrings.getLocalString("admin.set.attribute.change.failure", "Could not change the attributes: {0}",
                                        transactionFailure.getMessage()), transactionFailure);
                                return false;
                            }
                        } else {
                            changes.put((ConfigBean) node.getKey(), attrChanges);
                        }

                    }
                }
            }

2>

try {
                config.apply(changes);
                success(context, targetName, value);
                runLegacyChecks(context);
            } catch (TransactionFailure transactionFailure) {
                //fail(context, "Could not change the attributes: " +
                //        transactionFailure.getMessage(), transactionFailure);
                fail(context, localStrings.getLocalString("admin.set.attribute.change.failure", "Could not change the attributes: {0}",
                        transactionFailure.getMessage()), transactionFailure);
                return false;
            }

However, I think we should check whether the changes will affect the project of GF, for example, the duplicated context root shouldn't be set into the domain.xml. we should prevent the attribute set into domain.xml if it is unavailable.
BTW:I tried to roll back the operation, but it seems the changes were pretty involving and extensive. I think it is better to prevent the unavailable setting in domain.xml. I wonder if you can give me some advices about it.

Thanks.


Hong Zhang added a comment - 27/Sep/12 03:33 PM

Jeremy: as it's allowed to have same context root for different applications (they just cannot be deployed to the same virtual server on the same server instance), it's going to be hard for the set command to do this type of validation while not having any context root specific information (as it's a generic command). With the current code, can the second application be undeployed after the set context root operation failed (I hope the first application is at least not impacted by this failed set)? If yes, maybe the best fix would be just to provide a good error message after the set failed and ask user to redeploy the second application with a different context root (as you said, the roll back of the domain.xml change is going to be hard so we will need to ask user's help for that)?


Jeremy_Lv added a comment - 28/Sep/12 01:27 AM - edited

Hong:

With the current code, can the second application be undeployed after the set context root operation failed (I hope the first application is at least not impacted by this failed set)?

Yes, the first application will not be impacted by failed set about the second application.
The second application can't be undeployed.


maybe the best fix would be just to provide a good error message after the set failed and ask user to redeploy the second application with a different context root (as you said, the roll back of the domain.xml change is going to be hard so we will need to ask user's help for that)?

I think it could be a kind solution to provide a good error message after set failed and ask user to redeploy the second application with a different context root.


I will try to find out where is best to provide the error messages.
Thanks a lot.


Jeremy_Lv added a comment - 28/Sep/12 05:45 AM

Hong:
I have found that the error messages thrown by WebContainer#loadWebModule didn't displayed in the GUI when you edit the context root.


I have looked the code and found out the basic logical about setting the context root as follows:
1)It will execute the code in SetCommand#set to write the changes into the domain.xml.

try {
                config.apply(changes);
                success(context, targetName, value);
                runLegacyChecks(context);
            } catch (TransactionFailure transactionFailure) {
                //fail(context, "Could not change the attributes: " +
                //        transactionFailure.getMessage(), transactionFailure);
                fail(context, localStrings.getLocalString("admin.set.attribute.change.failure", "Could not change the attributes: {0}",
                        transactionFailure.getMessage()), transactionFailure);
                return false;
            }

2)After setting the changes into domain.xml successfully in the component of HK2, it will called the code in ApplicationLifecycle#deploy and thrown out the error messages about duplicated context root in WebContainer#loadWebModule.

} catch(Throwable loadException) {
        logger.log(Level.SEVERE, loadException.getMessage(), loadException);
        report.failure(logger, "Exception while loading the app", null);
        report.setFailureCause(loadException);
        tracker.actOn(logger);
        return null;
    }

3)after checking the exception about loading the app, it will return to the SetCommand#set. because it is normal to set the changes into domain.xml, the code as follows execute successfully without throwing any exception.

try {
                config.apply(changes);
                success(context, targetName, value);
                runLegacyChecks(context);
            } catch (TransactionFailure transactionFailure) {
                //fail(context, "Could not change the attributes: " +
                //        transactionFailure.getMessage(), transactionFailure);
                fail(context, localStrings.getLocalString("admin.set.attribute.change.failure", "Could not change the attributes: {0}",
                        transactionFailure.getMessage()), transactionFailure);
                return false;
            }

All in all, I think the successful messages thrown by SetCommand#set covered the error messages thrown by web container and deployment. Maybe we still should make changes in this module. I wonder if you can give another advice bout it.
Thanks a lot.


Hong Zhang added a comment - 04/Oct/12 01:50 PM

Thanks Jeremy for the investigation. Yes, the config change will trigger the application reloading through ApplicationConfigListener. And I am not sure how to make the exception thrown by a listener (in this case, ApplicationConfigListener implements TransactionListener) to propagate back to the user.

Tom: what do you think? In this user scenario, the user tries to set a context root to a value that's already in use, and the config change of setting this context root triggers the ApplicationConfigListener to reload the application and resulted a failure in web container when loading the component (which is an expected failure), but how do we inform the user about this failure so they could take action (to redeploy/undeploy the application).


Jeremy_Lv added a comment - 10/Oct/12 08:31 AM

Tom:
If you have cycle, please have a look at this issue.
I wonder if you can give us some suggestions.
Thanks


Tom Mueller added a comment - 12/Oct/12 05:40 PM

ApplicationConfigListener implements an HK2 TransactionListener. In HK2, a TransactionListener just finds out what happened in a transaction, but it has no ability to prevent a transaction from committing.

The way to prevent a transaction on a config bean from committing is to implement bean validation on the config bean and have the validation fail. Can you implement a bean validator for the Application.setContextRoot method so that it checks to see if the context root is unique for the virtual servers that are hosting that application, and if it is not then fail the validation. This will cause the transaction to fail, so the application will never be reloaded.


Hong Zhang added a comment - 16/Oct/12 01:21 PM

Thanks Tom for the suggestion. One issue with the validation logic is the logic is going to be fairly complex (as we need to look at the target server, virtual server etc), this was the main reason that we did not have validation logic right now at the earlier stage of the deployment lifecycle. Currently only the web container detects this type of error during loading. Maybe we just have to implement this logic so we don't run into this type of situation as there does not seem to be other easy workaround here. I will reassign to Shingwai to see what he thinks.


Amy Roh added a comment - 12/Mar/13 04:16 PM

Fixed in svn 60360.