org.glassfish.ejb.startup.SingletonLifeCycleManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of payara-micro Show documentation
Show all versions of payara-micro Show documentation
Micro Distribution of the Payara Project
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2009-2012 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.
*/
// Portions Copyright [2020] [Payara Foundation and/or its affiliates]
package org.glassfish.ejb.startup;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sun.ejb.containers.AbstractSingletonContainer;
import com.sun.enterprise.deployment.Application;
import com.sun.enterprise.deployment.BundleDescriptor;
import com.sun.logging.LogDomains;
import org.glassfish.ejb.deployment.descriptor.EjbDescriptor;
import org.glassfish.ejb.deployment.descriptor.EjbSessionDescriptor;
/**
* @author Mahesh Kannan
* Date: Aug 6, 2008
*/
public class SingletonLifeCycleManager {
private Map> initialDependency =
new HashMap>();
private Map name2Index = new HashMap();
private Map index2Name = new HashMap();
private Set leafNodes = new HashSet();
private int maxIndex = 0;
private boolean adj[][];
// List of eagerly initialized singletons, in the order they were
// initialized.
private List initializedSingletons =
new ArrayList();
private Map name2Container =
new HashMap();
private boolean initializeInOrder;
private Map name2EjbApp = new HashMap();
private static final Logger _logger =
LogDomains.getLogger(SingletonLifeCycleManager.class, LogDomains.EJB_LOGGER);
SingletonLifeCycleManager(boolean initializeInOrder) {
this.initializeInOrder = initializeInOrder;
}
void addSingletonContainer(EjbApplication ejbApp, AbstractSingletonContainer c) {
c.setSingletonLifeCycleManager(this);
EjbSessionDescriptor sdesc = (EjbSessionDescriptor) c.getEjbDescriptor();
String src = normalizeSingletonName(sdesc.getName(), sdesc);
String[] depends = sdesc.getDependsOn();
String[] newDepends = new String[depends.length];
StringBuilder sb = new StringBuilder("Partial order of dependent(s). " + src + " => {");
for(int i=0; i < depends.length; i++) {
newDepends[i] = normalizeSingletonName(depends[i], sdesc);
sb.append(newDepends[i] + " ");
}
sb.append("}");
if (_logger.isLoggable(Level.FINE)) {
_logger.log(Level.FINE, sb.toString());
}
this.addDependency(src, newDepends);
name2Container.put(src, c);
name2EjbApp.put(src, ejbApp);
}
private String normalizeSingletonName(String origName, EjbSessionDescriptor sessionDesc) {
String normalizedName;
boolean fullyQualified = origName.contains("#");
Application app = sessionDesc.getEjbBundleDescriptor().getApplication();
if (fullyQualified) {
int indexOfHash = origName.indexOf('#');
String ejbName = origName.substring(indexOfHash + 1);
String relativeJarPath = origName.substring(0, indexOfHash);
BundleDescriptor bundle = app.getRelativeBundle(sessionDesc.getEjbBundleDescriptor(),
relativeJarPath);
if (bundle == null) {
throw new IllegalStateException("Invalid @DependOn value = " + origName +
" for Singleton " + sessionDesc.getName());
}
normalizedName = bundle.getModuleDescriptor().getArchiveUri() + "#" + ejbName;
} else {
normalizedName = sessionDesc.getEjbBundleDescriptor().getModuleDescriptor().getArchiveUri() +
"#" + origName;
}
return normalizedName;
}
void doStartup(EjbApplication ejbApp) {
Collection ejbs = ejbApp.getEjbBundleDescriptor().getEjbs();
for (EjbDescriptor desc : ejbs) {
if (desc instanceof EjbSessionDescriptor) {
EjbSessionDescriptor sdesc = (EjbSessionDescriptor) desc;
if ((sdesc.isSingleton())) {
if (sdesc.getInitOnStartup()) {
String normalizedSingletonName = normalizeSingletonName(sdesc.getName(), sdesc);
initializeSingleton(name2Container.get(normalizedSingletonName));
}
}
}
}
}
void doShutdown() {
// Shutdown singletons in the reverse order of their initialization
Collections.reverse(initializedSingletons);
for(AbstractSingletonContainer singletonContainer : initializedSingletons) {
singletonContainer.onShutdown();
}
}
public synchronized void initializeSingleton(AbstractSingletonContainer c) {
initializeSingleton(c, new ArrayList<>());
}
private void initializeSingleton(AbstractSingletonContainer c, List initList) {
if (! initializedSingletons.contains(c)) {
String normalizedSingletonName = normalizeSingletonName(c.getEjbDescriptor().getName(),
(EjbSessionDescriptor) c.getEjbDescriptor());
List computedDeps = computeDependencies(normalizedSingletonName);
int sz = computedDeps.size();
AbstractSingletonContainer[] deps = new AbstractSingletonContainer[sz];
initList.add(normalizedSingletonName);
for (int i = 0; i < sz; i++) {
String nextSingletonName = computedDeps.get(i);
deps[i] = name2Container.get(nextSingletonName);
if (initializeInOrder) {
EjbApplication ejbApp = name2EjbApp.get(nextSingletonName);
if (! ejbApp.isStarted()) {
String msg = "application.xml specifies module initialization ordering but "
+ initList.get(0) + " depends on " + nextSingletonName
+ " which is in a module that hasn't been started yet";
if (_logger.isLoggable(Level.WARNING)) {
StringBuilder sb = new StringBuilder(initList.get(0));
for (int k=1; k ").append(initList.get(k));
}
sb.append(" -> ").append(nextSingletonName);
_logger.log(Level.WARNING, "Partial order of singleton beans involved in this: "
+ sb.toString());
}
throw new RuntimeException(msg);
}
}
initializeSingleton(deps[i], initList);
}
if (_logger.isLoggable(Level.FINE)) {
_logger.log(Level.FINE, "SingletonLifeCycleManager: initializingSingleton: " + normalizedSingletonName);
}
c.instantiateSingletonInstance();
initializedSingletons.add(c);
}
}
private void addDependency(String src, String[] depends) {
if (depends != null && depends.length > 0) {
for (String s : depends) {
addDependency(src, s);
}
} else {
addDependency(src, "");
}
}
private void addDependency(String src, String depends) {
src = src.trim();
Set deps = getExistingDependecyList(src);
if (depends != null) {
StringTokenizer tok = new StringTokenizer(depends, " ,");
while (tok.hasMoreTokens()) {
String dep = tok.nextToken();
deps.add(dep);
getExistingDependecyList(dep);
}
}
}
private List computeDependencies(String root) {
if (adj == null) {
fillAdjacencyMatrix();
}
Stack stk = new Stack();
stk.push(root);
List dependencies = new ArrayList();
do {
String top = stk.peek();
int topIndex = name2Index.get(top);
boolean hasDep = false;
for (int j = 0; j < maxIndex; j++) {
if (adj[topIndex][j]) {
String name = index2Name.get(j);
if (stk.contains(name)) {
String str = "Cyclic dependency: " + top + " => " + name + "? ";
throw new IllegalArgumentException(
str + getCyclicString(adj));
} else {
if (!dependencies.contains(name)) {
if (leafNodes.contains(name)) {
dependencies.add(name);
} else {
hasDep = true;
stk.push(name);
}
}
}
}
}
if (!hasDep) {
stk.pop();
if (!dependencies.contains(top)) {
dependencies.add(top);
}
}
} while (!stk.empty());
dependencies.remove(dependencies.size() - 1);
return dependencies;
}
private Set getExistingDependecyList(String src) {
Set existingDeps = initialDependency.get(src);
if (existingDeps == null) {
existingDeps = new HashSet();
initialDependency.put(src, existingDeps);
name2Index.put(src, maxIndex);
index2Name.put(maxIndex, src);
maxIndex++;
}
return existingDeps;
}
private void fillAdjacencyMatrix() {
adj = new boolean[maxIndex][maxIndex];
for (int i = 0; i < maxIndex; i++) {
String src = index2Name.get(i);
for (int j = 0; j < maxIndex; j++) {
adj[i][j] = false;
}
boolean isLeaf = true;
Set deps = initialDependency.get(src);
for (String d : deps) {
int k = name2Index.get(d);
adj[i][k] = true;
isLeaf = false;
}
if (isLeaf) {
leafNodes.add(src);
}
}
}
private String getCyclicString(boolean[][] a) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < maxIndex; i++) {
StringBuilder sb2 = new StringBuilder("");
String delim = "";
for (int j = 0; j < maxIndex; j++) {
if (a[i][j]) {
sb2.append(delim).append(index2Name.get(j));
delim = ", ";
}
}
String dep = sb2.toString();
if (dep.length() > 0) {
sb.append(" ").append(index2Name.get(i))
.append(" => ").append(sb2.toString()).append("; ");
}
}
return sb.toString();
}
/*
public static void main(String[] args) {
test1();
test2();
test3();
test4();
}
private static void test1() {
SingletonLifeCycleManager ts = new SingletonLifeCycleManager(false);
ts.addDependency("A", "B, C");
ts.addDependency("B", "C, D");
ts.addDependency("D", "E");
ts.addDependency("C", "D, E, G");
ts.addDependency("E", "D");
ts.getPartialOrdering();
SingletonLifeCycleManager t = new SingletonLifeCycleManager(false);
t.addDependency("C", ts.computeDependencies("C"));
t.addDependency("D", ts.computeDependencies("D"));
t.printAdjacencyMatrix();
for (String s : t.computeDependencies("C")) {
System.out.print(s + " ");
}
System.out.println();
for (String s : t.getPartialOrdering()) {
System.out.print(s + " ");
}
}
private static void test2() {
SingletonLifeCycleManager ts = new SingletonLifeCycleManager(false);
ts.addDependency("A", "D, E");
ts.addDependency("B", "F");
ts.addDependency("C", "G, H");
ts.addDependency("D", "I");
ts.addDependency("E", "J");
ts.addDependency("F", "J, K");
ts.addDependency("H", "L");
ts.addDependency("I", "M, N");
ts.addDependency("J", "U");
ts.addDependency("K", "U");
ts.addDependency("L", "O");
ts.addDependency("U", "N, O");
ts.addDependency("N", "P, Q");
ts.addDependency("O", "Q, R");
ts.addDependency("Q", "S, T");
ts.addDependency("E", "X, W");
ts.addDependency("X", "Y");
ts.addDependency("W", "Y");
ts.addDependency("Y", "Z");
ts.addDependency("Z", "O");
//ts.addDependency("R", "J");
String[] dep = ts.getPartialOrdering();
for (String s : dep) {
System.out.print(s + " ");
}
System.out.println();
SingletonLifeCycleManager ts2 = new SingletonLifeCycleManager(false);
ts2.addDependency("E", ts.computeDependencies("E"));
ts2.addDependency("U", ts.computeDependencies("U"));
ts2.addDependency("H", ts.computeDependencies("H"));
for (String s : ts2.getPartialOrdering()) {
System.out.print(s + " ");
}
}
private static void test3() {
SingletonLifeCycleManager ts = new SingletonLifeCycleManager(false);
ts.addDependency("A", (String) null);
ts.addDependency("B", (String) null);
ts.addDependency("C", (String) null);
String[] dep = ts.getPartialOrdering();
for (String s : dep) {
System.out.print(s + " ");
}
System.out.println();
for (String s : ts.computeDependencies("B")) {
System.out.print(s + " ");
}
System.out.println();
}
private static void test4() {
SingletonLifeCycleManager ts = new SingletonLifeCycleManager(false);
ts.addDependency("A", "D, B, C");
ts.addDependency("B", "F, I");
ts.addDependency("C", "F, H, G");
ts.addDependency("D", "E");
ts.addDependency("E", (String) null);
ts.addDependency("F", "E");
ts.addDependency("G", "E, I, K");
ts.addDependency("H", "J");
ts.addDependency("I", "J");
ts.addDependency("K", (List) null);
String[] dep = ts.getPartialOrdering();
for (String s : dep) {
System.out.print(s + " ");
}
System.out.println();
for (String s : ts.computeDependencies("C")) {
System.out.print(s + " ");
}
System.out.println();
}
private String[] getPartialOrdering() {
if (adj == null) {
fillAdjacencyMatrix();
}
boolean[][] tempAdj = new boolean[maxIndex][maxIndex];
for (int i = 0; i < maxIndex; i++) {
for (int j = 0; j < maxIndex; j++) {
tempAdj[i][j] = adj[i][j];
}
}
List dependencies = new ArrayList();
do {
String src = null;
boolean foundAtLeastOneLeaf = false;
for (int i = 0; i < maxIndex; i++) {
src = index2Name.get(i);
if (!dependencies.contains(src)) {
boolean hasDep = false;
for (int j = 0; j < maxIndex; j++) {
if (tempAdj[i][j]) {
hasDep = true;
break;
}
}
if ((!hasDep) && (!dependencies.contains(src))) {
dependencies.add(src);
for (int k = 0; k < maxIndex; k++) {
tempAdj[k][i] = false;
}
foundAtLeastOneLeaf = true;
}
}
}
if ((!foundAtLeastOneLeaf) && (dependencies.size() < name2Index.size())) {
throw new IllegalArgumentException("Circular dependency: "
+ getCyclicString(tempAdj));
}
} while (dependencies.size() < name2Index.size());
return dependencies.toArray(new String[dependencies.size()]);
}
private void addDependency(String src, List depends) {
if (depends != null) {
for (String s : depends) {
addDependency(src, s);
}
} else {
addDependency(src, "");
}
}
private void printAdjacencyMatrix() {
if (adj == null) {
fillAdjacencyMatrix();
}
System.out.print(" ");
for (int i = 0; i < maxIndex; i++) {
System.out.print(" " + index2Name.get(i));
}
System.out.println();
for (int i = 0; i < maxIndex; i++) {
System.out.print(index2Name.get(i) + " ");
for (int j = 0; j < maxIndex; j++) {
System.out.print(adj[i][j] ? "1 " : "0 ");
}
System.out.println();
}
}
*/
}