
org.glassfish.admingui.plugin.ConsolePluginService Maven / Gradle / Ivy
Show all versions of console-plugin-service Show documentation
/*
* Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package org.glassfish.admingui.plugin;
import jakarta.inject.Inject;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.glassfish.admingui.connector.ConsoleConfig;
import org.glassfish.admingui.connector.Index;
import org.glassfish.admingui.connector.IndexItem;
import org.glassfish.admingui.connector.IntegrationPoint;
import org.glassfish.admingui.connector.TOC;
import org.glassfish.admingui.connector.TOCItem;
import org.glassfish.api.admingui.ConsoleProvider;
import org.glassfish.hk2.api.IterableProvider;
import org.glassfish.hk2.api.ServiceLocator;
import org.jvnet.hk2.annotations.Service;
import org.jvnet.hk2.component.MultiMap;
import org.jvnet.hk2.config.ConfigParser;
import org.jvnet.hk2.config.DomDocument;
/**
*
* This class provides access to {@link IntegrationPoint}s.
*
*
* @author Ken Paulsen ([email protected])
*/
@Service
public class ConsolePluginService {
@Inject
Logger logger;
@Inject
ServiceLocator habitat;
@Inject
IterableProvider providers;
/**
* Default constructor.
*/
public ConsolePluginService() {
}
/**
* Initialize the available {@link IntegrationPoint}s.
*/
protected synchronized void init() {
if (initialized) {
return;
}
initialized = true;
// First find the parser
if ((providers != null) && (providers.iterator().hasNext())) {
// Get our parser...
ConfigParser parser = new ConfigParser(habitat);
String id = null;
Map configUrls = new HashMap<>();
// Loop through the configs and add them all
for (ConsoleProvider provider : providers) {
// Read the contents from the URL
URL url = provider.getConfiguration();
Iterator urlsIterator;
if (url != null) {
urlsIterator = List.of(url).iterator();
} else {
try {
urlsIterator = provider.getClass().getClassLoader().getResources(
ConsoleProvider.DEFAULT_CONFIG_FILENAME).asIterator();
} catch (IOException ex) {
throw new RuntimeException(ex);
}
if (!urlsIterator.hasNext()) {
logger.log(Level.INFO, "Unable to find "
+ ConsoleProvider.DEFAULT_CONFIG_FILENAME
+ " file for provider '"
+ provider.getClass().getName() + "'");
continue;
}
}
while (urlsIterator.hasNext()) {
configUrls.put(urlsIterator.next(), provider.getClass().getClassLoader());
}
}
for (Map.Entry entry : configUrls.entrySet()) {
URL url = entry.getKey();
ClassLoader classLoader = entry.getValue();
//System.out.println("Provider *"+provider+"* : url=*"+url+"*");
DomDocument doc = parser.parse(url);
// Get the New IntegrationPoints
ConsoleConfig config = (ConsoleConfig) doc.getRoot().get();
// Save the ClassLoader for later
//System.out.println("Storing: " + config.getId() + " : " + provider.getClass().getClassLoader());
id = config.getId();
moduleClassLoaderMap.put(id, classLoader);
classLoaderModuleMap.put(classLoader, id);
// Add the new IntegrationPoints
addIntegrationPoints(config.getIntegrationPoints(), id);
}
}
//System.out.println("IP Map: " + pointsByType.toString());
// Log some trace messages
logger.log(Level.CONFIG, "Console Plugin Service has been initialized. Integration points by type: \n{0}", pointsByType);
}
/**
* This method returns a merged Table Of Contents for all found help sets for the given locale.
*/
public synchronized TOC getHelpTOC(String locale) {
if (locale == null) {
locale = "en"; // Use this as the default...
}
// TOC
Map> mapUrls = getResources(locale + "/help/toc.xml");
// Get our parser...
ConfigParser parser = new ConfigParser(habitat);
// Setup a new "merged" TOC...
var mergedTOC = new TOC();
mergedTOC.setTOCItems(new ArrayList());
mergedTOC.setVersion("2.0");
// Loop through the urls and add them all
String id = null; // module id
String prefix = "/" + locale + "/help/"; // prefix (minus module id)
List urls = null; // URLs to TOC files w/i each plugin module
for (Map.Entry> entry : mapUrls.entrySet()) {
id = entry.getKey();
urls = entry.getValue();
for (URL url : urls) {
DomDocument doc = parser.parse(url);
// Merge all the TOC's...
TOC toc = (TOC) doc.getRoot().get();
for (TOCItem item : toc.getTOCItems()) {
insertTOCItem(mergedTOC.getTOCItems(), item, id + prefix);
}
}
}
// FIXME: Sort?
return mergedTOC;
}
/**
* This method inserts the given item
into the dest
list.
*/
private void insertTOCItem(List dest, TOCItem item, String prefix) {
int idx = dest.indexOf(item);
if (idx == -1) {
// Fix target path...
fixTargetPath(item, prefix);
// Not there yet, just add it...
dest.add(item);
} else {
// Already there, insert children of item...
TOCItem parent = dest.get(idx);
for (TOCItem child : item.getTOCItems()) {
insertTOCItem(parent.getTOCItems(), child, prefix);
}
}
}
/**
* This method returns a merged Table Of Contents for all found help sets for the given locale.
*/
public synchronized Index getHelpIndex(String locale) {
if (locale == null) {
locale = "en"; // Use this as the default...
}
// TOC
Map> mapUrls = getResources(locale + "/help/index.xml");
// Get our parser...
ConfigParser parser = new ConfigParser(habitat);
// Setup a new "merged" TOC...
var mergedIndex = new Index();
mergedIndex.setIndexItems(new ArrayList());
mergedIndex.setVersion("2.0");
// Loop through the urls and add them all
String id = null; // module id
String prefix = "/" + locale + "/help/"; // prefix (minus module id)
List urls = null; // URLs to TOC files w/i each plugin module
for (Map.Entry> entry : mapUrls.entrySet()) {
id = entry.getKey();
urls = entry.getValue();
for (URL url : urls) {
DomDocument doc = parser.parse(url);
// Merge all the TOC's...
Index index = (Index) doc.getRoot().get();
for (IndexItem item : index.getIndexItems()) {
insertIndexItem(mergedIndex.getIndexItems(), item, id + prefix);
}
}
}
// FIXME: Sort?
return mergedIndex;
}
/**
* This method inserts the given item
into the dest
list.
*/
private void insertIndexItem(List dest, IndexItem item, String prefix) {
int idx = dest.indexOf(item);
if (idx == -1) {
// Fix target path...
fixHtmlFileForIndexItem(item, prefix);
// Not there yet, just add it...
dest.add(item);
} else {
// Already there, insert children of item...
IndexItem parent = dest.get(idx);
for (IndexItem child : item.getIndexItems()) {
insertIndexItem(parent.getIndexItems(), child, prefix);
}
}
}
/**
*
*/
private void fixTargetPath(TOCItem parent, String prefix) {
parent.setTargetPath(prefix + parent.getTarget() + ".html");
for (TOCItem item : parent.getTOCItems()) {
fixTargetPath(item, prefix);
}
}
/**
*
*/
private void fixHtmlFileForIndexItem(IndexItem parent, String prefix) {
String target = null;
if (null != (target = parent.getTarget())) {
parent.setHtmlFileForTarget(prefix + target + ".html");
}
for (IndexItem item : parent.getIndexItems()) {
fixHtmlFileForIndexItem(item, prefix);
}
}
/**
*
* This method searches the classpath of all plugins for the requested resource and returns all instances of it (if
* any). This method will NOT return null
, but may return an empty List
.
*
*/
public Map> getResources(String name) {
Map> result = new HashMap>();
if ((providers != null) && (providers.iterator().hasNext())) {
// Get our parser...
Enumeration urls = null;
URL url = null;
// Loop through the configs and add them all
for (ConsoleProvider provider : providers) {
// Read the contents from the URL
ClassLoader loader = provider.getClass().getClassLoader();
try {
urls = loader.getResources(name);
} catch (IOException ex) {
logger.log(Level.INFO, "Error getting resource '"
+ name + "' from provider: '"
+ provider.getClass().getName() + "'. Skipping...",
ex);
continue;
}
List providerURLs = new ArrayList();
while (urls.hasMoreElements()) {
// Found one... add it.
url = urls.nextElement();
try {
providerURLs.add(new URL(url, ""));
} catch (Exception ex) {
// Ignore b/c this should not ever happen, we're not
// changing the URL
logger.log(Level.SEVERE, "ConsolePluginService: URL Copy Failed!", ex);
}
}
// Put the URLs into the Map by module id...
if (providerURLs.size() > 0) {
result.put(classLoaderModuleMap.get(loader), providerURLs);
}
}
}
return result;
}
/***********************************************************
public static byte[] readFromURL(URL url) throws IOException {
byte buffer[] = new byte[10000];
byte result[] = new byte[0];
int count = 0;
int offset = 0;
java.io.InputStream in = url.openStream();
// Attempt to read up to 10K bytes.
count = in.read(buffer);
while (count != -1) {
// Make room for new content...
//result = Arrays.copyOf(result, offset + count); Java 6 only...
// When I can depend on Java 6... replace the following 3 lines
// with the line above.
byte oldResult[] = result;
result = new byte[offset + count];
System.arraycopy(oldResult, 0, result, 0, offset);
// Copy in new content...
System.arraycopy(buffer, 0, result, offset, count);
// Increment the offset
offset += count;
// Attempt to read up to 10K more bytes...
count = in.read(buffer);
}
return result;
}
***********************************************************/
/**
* This method allows new {@link IntegrationPoint}s to be added to the known {@link IntegrationPoint}s.
*/
public void addIntegrationPoints(List points, String id) {
// Add them all...
for (IntegrationPoint point : points) {
addIntegrationPoint(point, id);
}
}
/**
* This method allows a new {@link IntegrationPoint} to be added to the known {@link IntegrationPoint}s.
*/
public void addIntegrationPoint(IntegrationPoint point, String id) {
// Associate the Provider with this IntegrationPoint so we
// have a way to get the correct classloader
point.setConsoleConfigId(id);
// Add it
pointsByType.add(point.getType(), point);
}
/**
*
* This method returns the {@link IntegrationPoint}s associated with the given type.
*
*
* @param type The type of {@link IntegrationPoint}s to retrieve.
*/
public List getIntegrationPoints(String type) {
init(); // Ensure it is initialized.
return pointsByType.get(type);
}
/**
*
* This method returns the ClassLoader
associated with the requested module. If the requested module does
* not exist, has not been initialized, or does not contain any admin console extensions, this method will return
* null
.
*
*
* @param moduleName The name of the module.
*
* @return null
, or the module's ClassLoader
.
*/
public ClassLoader getModuleClassLoader(String moduleName) {
return moduleClassLoaderMap.get(moduleName);
}
/**
* Flag indicating intialization has already occured.
*/
private boolean initialized = false;
/**
* This Map
contains the {@link IntegrationPoint}s keyed by the type
of integration.
*/
private MultiMap pointsByType = new MultiMap();
/**
*
* This Map
keeps track of the ClassLoader
for each module that provides GUI
* {@link IntegrationPoint}s. It is keyed by the id specified in the console-config.xml
file from the
* module.
*
*/
private Map moduleClassLoaderMap = new HashMap();
/**
*
* This Map
keeps track of the module id for each ClassLoader
that provides
* {@link IntegrationPoint}s. It is keyed by the classloader and returns the module id as specified in the module's
* console-config.xml
file.
*
*/
private Map classLoaderModuleMap = new HashMap();
}