All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glassfish.web.deployment.descriptor.OrderingDescriptor Maven / Gradle / Ivy

The newest version!
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package org.glassfish.web.deployment.descriptor;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

import com.sun.enterprise.util.LocalStringManagerImpl;
import org.glassfish.deployment.common.Descriptor;

/**
 * This represents the ordering resided in web-fragment.xml.
 *
 * @author Shing Wai Chan
 */

public class OrderingDescriptor extends Descriptor {
    private static LocalStringManagerImpl localStrings =
            new LocalStringManagerImpl(OrderingDescriptor.class);

    OrderingOrderingDescriptor after = null;
    
    OrderingOrderingDescriptor before = null;

    public OrderingOrderingDescriptor getAfter() {
        return after;
    }

    public void setAfter(OrderingOrderingDescriptor after) {
        this.after = after;
        validate();
    }

    public OrderingOrderingDescriptor getBefore() {
        return before;
    }

    public void setBefore(OrderingOrderingDescriptor before) {
        this.before = before;
        validate();
    }

    public void validate() {
        boolean valid = true;
        if (after != null && before != null) {
            if (after.containsOthers() && before.containsOthers()) {
                valid = false;
            }
            if (valid) {
                for (String name : after.getNames()) {
                    if (before.containsName(name)) {
                        valid = false;
                        break;
                    }
                }
            }
        }

        if (!valid) {
            throw new IllegalStateException(localStrings.getLocalString(
                    "web.deployment.exceptioninvalidordering",
                    "The ordering is not valid as it contains the same name and/or others in both before and after."));
        }

    }

    public String toString() {
        StringBuilder builder = new StringBuilder();
        if (after != null) {
            builder.append("after: " + after + ", ");
        }
        if (before != null) {
            builder.append("before: " + before);
        }
        return builder.toString();
    }


    // ----- sorting logic

    public static void sort(List wfs) {
        if (wfs == null || wfs.size() <= 1) {
            return;
        }

        // build the graph
        List graph = new ArrayList();
        Map name2NodeMap = new HashMap();

        // build the nodes
        Node othersNode = new Node(null);
        for (WebFragmentDescriptor wf : wfs) {
            Node wfNode = new Node(wf);
            String wfName = wf.getName();
            if (wfName != null && wfName.length() > 0) {
                name2NodeMap.put(wfName, wfNode);
            }
            graph.add(wfNode);
        }

        List remaining = new ArrayList(graph);

        // build the edges
        // othersNode is not in the loop
        Map> map = new HashMap>();
        for (int i = 0; i < graph.size(); i++) {
            Node wfNode = graph.get(i);
            WebFragmentDescriptor wf = wfNode.getWebFragmentDescriptor();
            String wfName = wf.getName();
            OrderingDescriptor od = wf.getOrderingDescriptor();
            if (od != null) {
                OrderingOrderingDescriptor after = od.getAfter();
                if (after != null) {
                    if (after.containsOthers()) {
                        // othersNode --> wfNode
                        wfNode.getInEdges().add(getEdge(othersNode, wfNode, map));
                        othersNode.getOutEdges().add(getEdge(othersNode, wfNode, map));
                        remaining.remove(othersNode);
                    }
                    for (String name : after.getNames()) {
                        // nameNode --> wfNode
                        Node nameNode = name2NodeMap.get(name);
                        if (nameNode != null) {
                            wfNode.getInEdges().add(
                                    getEdge(nameNode, wfNode, map));
                            nameNode.getOutEdges().add(
                                    getEdge(nameNode, wfNode, map));
                            remaining.remove(nameNode);
                        }
                    }
                }

                OrderingOrderingDescriptor before = od.getBefore();
                if (before != null) {
                    if (before.containsOthers()) {
                        // wfNode --> othersNode
                        wfNode.getOutEdges().add(getEdge(wfNode, othersNode, map));
                        othersNode.getInEdges().add(getEdge(wfNode, othersNode, map));
                        remaining.remove(othersNode);
                    }
                    for (String name : before.getNames()) {
                        // wfNode --> nameNode
                        Node nameNode = name2NodeMap.get(name);
                        if (nameNode != null) {
                            wfNode.getOutEdges().add(
                                    getEdge(wfNode, nameNode, map));
                            nameNode.getInEdges().add(
                                    getEdge(wfNode, nameNode, map));
                            remaining.remove(nameNode);
                        }
                    }
                }

                boolean hasAfterOrdering = (after != null &&
                        (after.containsOthers() || after.getNames().size() > 0));
                boolean hasBeforeOrdering = (before != null &&
                        (before.containsOthers() || before.getNames().size() > 0));
                if (hasAfterOrdering || hasBeforeOrdering) {
                    remaining.remove(wfNode);
                }
            }
        }

        // if others should be in the graph
        if (othersNode.getOutEdges().size() > 0 || othersNode.getInEdges().size() > 0) {
            // add others to the end
            graph.add(othersNode);

            // populate others info into nodes
            Stack afterOthersStack = new Stack();
            for (Edge edge : othersNode.getOutEdges()) {
                edge.setVisited(true);
                afterOthersStack.push(edge.getToNode());
            }
            while (!afterOthersStack.isEmpty()) {
                Node aNode = afterOthersStack.pop();
                aNode.setAfterOthers(true);
                for (Edge edge : aNode.getOutEdges()) {
                    if (!edge.isVisited()) {
                        afterOthersStack.push(edge.getToNode());
                        edge.setVisited(true);
                    }
                }
            }

            Stack beforeOthersStack = new Stack();
            for (Edge edge : othersNode.getInEdges()) {
                edge.setVisited(true);
                beforeOthersStack.push(edge.getFromNode());
            }
            while (!beforeOthersStack.isEmpty()) {
                Node aNode = beforeOthersStack.pop();
                aNode.setBeforeOthers(true);
                for (Edge edge : aNode.getInEdges()) {
                    if (!edge.isVisited()) {
                        beforeOthersStack.push(edge.getFromNode());
                        edge.setVisited(true);
                    }
                }
            }
        }

        List subgraph = new ArrayList(graph);
        subgraph.removeAll(remaining);
        boolean hasRemaining = (remaining.size() > 0);

        List sortedNodes = topologicalSort(subgraph, hasRemaining);
        wfs.clear();
        boolean othersProcessed = false;
        for (Node node: sortedNodes) {
            if (node.isOthers()) {
                // others
                othersProcessed = true;
                for (Node rnode: remaining) {
                    wfs.add(rnode.getWebFragmentDescriptor());
                }
            } else {
                wfs.add(node.getWebFragmentDescriptor());
            }
        }

        if (!othersProcessed) {
            for (Node rnode: remaining) {
                wfs.add(rnode.getWebFragmentDescriptor());
            }
        }
    }

    private static Edge getEdge(Node from, Node to, Map> map) {
        Edge edge = null;
        Map node2Edge = map.get(from);
        if (node2Edge == null) {
            node2Edge = new HashMap();
            map.put(from, node2Edge);
        } else {
            edge = node2Edge.get(to);
        }
        if (edge == null) {
            edge = new Edge(from, to);
            node2Edge.put(to, edge);
        }
        return edge;
    }

    /**
     * Note that this processing will modify the graph.
     * It is not intended for public.
     * @param graph
     * @param hasRemaining
     * @return a sorted list of Node
     */
    private static List topologicalSort(List graph, boolean hasRemaining) {
        List sortedNodes = new ArrayList();

        if (graph.size() == 0) {
            return sortedNodes;
        }

        Stack roots = new Stack();
        Stack rootsBefore = new Stack();
        Stack rootsAfter = new Stack(); // including others if any
        // find nodes without incoming edges
        for (Node node: graph) {
            if (node.getInEdges().size() == 0) {
                if (node.isBeforeOthers()) {
                    rootsBefore.add(node);
                } else if (node.isAfterOthers() || node.isOthers()) {
                    rootsAfter.add(node);
                } else {
                    roots.add(node);
                }
            }
        }
        
        if (roots.empty() && rootsBefore.empty() && rootsAfter.empty()) {
            // check if it is a circle with others and empty remaining
            if (isCircleWithOthersAndNoRemaining(graph, hasRemaining, sortedNodes)) {
                return sortedNodes;
            } else {
                throw new IllegalStateException(localStrings.getLocalString(
                        "web.deployment.exceptioninvalidwebfragmentordering",
                        "The web fragment ordering is not valid and possibly has cycling conflicts."));
            }
        }

        while (!(roots.empty() && rootsBefore.empty() && rootsAfter.empty())) {
            Node node = null;
            if (!rootsBefore.empty()) {
                node = rootsBefore.pop();
            } else if (!roots.empty()) {
                node = roots.pop();
            } else { // !rootsAfter.empty()
                node = rootsAfter.pop();
            } 

            sortedNodes.add(node);
            // for each outcoming edges
            Iterator outEdgesIter = node.getOutEdges().iterator();
            while (outEdgesIter.hasNext()) {
                Edge outEdge = outEdgesIter.next();
                Node outNode = outEdge.getToNode();
                // remove the outcoming edge
                outEdgesIter.remove();
                // remove corresponding incoming edge from the outNode
                outNode.getInEdges().remove(outEdge);

                // if no incoming edge
                if (outNode.getInEdges().size() == 0) {
                    // the others node
                    if (node.isBeforeOthers()) {
                        rootsBefore.add(outNode);
                    } else if (node.isAfterOthers() || node.isOthers()) {
                        rootsAfter.add(outNode);
                    } else {
                        roots.add(outNode);
                    }
                }
            }
        }

        boolean hasEdges = false;
        for (Node node: graph) {
            if (node.getInEdges().size() > 0 || node.getOutEdges().size() > 0) {
                hasEdges = true;
                break;
            }
        }
        if (hasEdges) {
            throw new IllegalStateException(localStrings.getLocalString(
                    "web.deployment.exceptioninvalidwebfragmentordering",
                    "The web fragment ordering is not valid and possibly has cycling conflicts."));
        }
        return sortedNodes;
    }

    /**
     * This method will check whether the graph does not have remaining vertices
     * and is a circle with others. It return the sorted result in sortedNodes.
     * @param graph  the input graph
     * @param hasRemaining
     * @param sortedNodes  output sorted result if it is a circle with empty others
     * @return boolean indicating whether it is a circle with an empty others
     */
    private static boolean isCircleWithOthersAndNoRemaining(List graph,
            boolean hasRemaining, ListsortedNodes) {

        boolean circleWithOthersAndNoRemaining = false;
        int size = graph.size();
        if (size == 0 || hasRemaining) {
            return circleWithOthersAndNoRemaining;
        }

        Node nextNode = graph.get(size - 1);
        if (nextNode.isOthers()) {
            Set set = new LinkedHashSet();
            int count = 0;
            while ((count < size) &&
                    nextNode.getOutEdges().size() == 1 &&
                    nextNode.getInEdges().size() == 1) {

                if (!set.add(nextNode)) {
                    break;
                }
                nextNode = nextNode.getOutEdges().iterator().next().getToNode();
                count++;
            }

            if (set.size() == size) {
                circleWithOthersAndNoRemaining = true;
                Iterator iter = set.iterator();
                // exclude others
                if (iter.hasNext()) {
                    iter.next();
                }
                while (iter.hasNext()) {
                   sortedNodes.add(iter.next());
                }
            }
        }

        return circleWithOthersAndNoRemaining;
    }

    // for debug
    private static void print(WebFragmentDescriptor wf,
            String nullWfString, StringBuilder sb) {

        String wfName = null;
        if (wf != null) {
            wfName = wf.getName();
        } else {
            wfName = nullWfString;
        }
        sb.append(wfName);
    }

    private static class Node {
        private WebFragmentDescriptor webFragmentDescriptor = null;
        private Set inEdges = new LinkedHashSet();
        private Set outEdges = new LinkedHashSet();
        // for sorting
        private boolean afterOthers = false;
        private boolean beforeOthers = false;

        private Node(WebFragmentDescriptor wf) {
            webFragmentDescriptor = wf;
        }

        private WebFragmentDescriptor getWebFragmentDescriptor() {
            return webFragmentDescriptor;
        }

        private Set getInEdges() {
            return inEdges;
        }

        private Set getOutEdges() {
            return outEdges;
        }

        private boolean isOthers() {
            return (webFragmentDescriptor == null);
        }

        private boolean isAfterOthers() {
            return afterOthers;
        }

        private void setAfterOthers(boolean afterOthers) {
            this.afterOthers = afterOthers;
        }

        private boolean isBeforeOthers() {
            return beforeOthers;
        }

        private void setBeforeOthers(boolean beforeOthers) {
            this.beforeOthers = beforeOthers;
        }

        // for debug
        public String toString() {
            StringBuilder sb = new StringBuilder("{name=");
            print(webFragmentDescriptor, "@others", sb);

            sb.append(", inNodes=[");
            boolean first = true;
            for (Edge e: inEdges) {
                if (!first) {
                    sb.append(", ");
                }
                first = false;
                print(e.getFromNode().getWebFragmentDescriptor(), "@others", sb);
            }
            sb.append("]");

            sb.append(", outNodes=[");
            first = true;
            for (Edge e: outEdges) {
                if (!first) {
                    sb.append(", ");
                }
                first = false;
                print(e.getToNode().getWebFragmentDescriptor(), "@others", sb);
            }
            sb.append("]}");
            return sb.toString();
        }
    }

    // an edge with data
    private static final class Edge {
        private Node fromNode = null;
        private Node toNode = null;
        private boolean visited = false;

        private Edge(Node from, Node to) {
            fromNode = from;
            toNode = to;
        }

        private Node getFromNode() {
            return fromNode;
        }

        private Node getToNode() {
            return toNode;
        }

        private boolean isVisited() {
            return visited;
        }

        private void setVisited(boolean vt) {
            visited = vt;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy