| /* |
| * Copyright (c) 1998, 2021 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, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // Contributors: |
| // Oracle - initial API and implementation from Oracle TopLink |
| package org.eclipse.persistence.sessions.factories; |
| |
| import java.io.File; |
| import java.net.URL; |
| import java.util.Map; |
| import java.util.Vector; |
| |
| import org.eclipse.persistence.exceptions.SessionLoaderException; |
| import org.eclipse.persistence.exceptions.ValidationException; |
| import org.eclipse.persistence.internal.helper.ConversionManager; |
| import org.eclipse.persistence.internal.localization.ExceptionLocalization; |
| import org.eclipse.persistence.internal.sessions.factories.PersistenceEntityResolver; |
| import org.eclipse.persistence.internal.sessions.factories.SessionsFactory; |
| import org.eclipse.persistence.internal.sessions.factories.XMLSessionConfigProject_11_1_1; |
| import org.eclipse.persistence.internal.sessions.factories.XMLSessionConfigToplinkProject; |
| import org.eclipse.persistence.internal.sessions.factories.model.SessionConfigs; |
| import org.eclipse.persistence.oxm.XMLContext; |
| import org.eclipse.persistence.oxm.XMLUnmarshaller; |
| import org.eclipse.persistence.platform.xml.XMLParser; |
| import org.eclipse.persistence.platform.xml.XMLPlatform; |
| import org.eclipse.persistence.platform.xml.XMLPlatformFactory; |
| import org.eclipse.persistence.sessions.Project; |
| import org.eclipse.persistence.sessions.Session; |
| import org.w3c.dom.Document; |
| import org.xml.sax.ErrorHandler; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.SAXParseException; |
| |
| /** |
| * Provide a mechanism for loading Session configuration XML files. |
| * This is used by the SessionManager to define how to find and load a Session from a sessions XML file. |
| * The sessions XML file is typically deployed in the applications jar (ejb-jar) and named sessions.xml in the /META-INF directory. |
| * Several loading options are provided, |
| * <ul> |
| * <li> resourceName : The resource path and file name to the sessions XML file, |
| * default is /sessions.xml or /META-INF/sessions.xml. (ensure "/" is used, not "\"). |
| * A file path can also be provided, although a resource is typically used. |
| * <li> shouldLogin : Define if the loaded session should be connected, default true. |
| * <li> shouldRefresh : Define if the loaded session should be refreshed from the file, |
| * (this old session will be disconnected) default false. |
| * <li> classLoader : Define the class loader that should be used to find the resource. |
| * This loader will also be used as the loaded session's class loader. This should be the application's class loader. |
| * Default is the ConversionManager loader, which is thread-based. |
| * <li> shouldCheckClassLoader : Defines if the session will be refreshed from the file if the class loader requesting the load, |
| * is different than the loaded session's class loader. This can be used to handle re-deployment. |
| * </ul> |
| * |
| * @since TopLink 10.1.3 |
| * @author Guy Pelletier |
| */ |
| public class XMLSessionConfigLoader { |
| protected String resourceName; |
| |
| /** Stores the resource path to provide a better error message if the load fails. */ |
| protected String resourcePath = DEFAULT_RESOURCE_NAME; |
| /** Stores the name of the Session in the sessions XML file desired to be loaded. */ |
| protected String sessionName = "default"; |
| /** Define if the loaded session should be connected, default true. */ |
| protected boolean shouldLogin = true; |
| /** Define if the loaded session should be refreshed from the file. */ |
| protected boolean shouldRefresh = false; |
| /** Define the class loader that should be used to find the resource. */ |
| protected ClassLoader classLoader; |
| /** Defines if the session will be refreshed from the file if the class loader requesting the load is different than the loaded session's class loader. */ |
| protected boolean shouldCheckClassLoader = false; |
| /** Stores any exceptions that occurred to provide all the exceptions up front if the load fails. */ |
| protected Vector<Throwable> exceptionStore; |
| /** Used to store the entity resolver to validate the XML schema when parsing. */ |
| protected PersistenceEntityResolver entityResolver; |
| |
| public static final String ECLIPSELINK_SESSIONS_SCHEMA = "org/eclipse/persistence/eclipselink_sessions_2.1.xsd"; |
| protected static final String DEFAULT_RESOURCE_NAME = "sessions.xml"; |
| protected static final String DEFAULT_RESOURCE_NAME_IN_META_INF = "META-INF/sessions.xml"; |
| |
| /** Cache the creation and initialization of the Session XML mapping project. */ |
| protected static final Project project = new XMLSessionConfigProject_11_1_1(); |
| |
| /** Cache the creation and initialization of the Session XML mapping project. */ |
| protected static Project getProject() { |
| return project; |
| } |
| |
| /** |
| * PUBLIC: |
| * This constructor is used when the file resource named 'sessions.xml' will |
| * be parsed for configuration. |
| */ |
| public XMLSessionConfigLoader() { |
| this(DEFAULT_RESOURCE_NAME); |
| } |
| |
| /** |
| * PUBLIC: |
| * This constructor is used when passing in the resource name of the |
| * configuration file that should be parsed |
| */ |
| public XMLSessionConfigLoader(String resourceName) { |
| this.resourceName = resourceName; |
| this.exceptionStore = new Vector<>(); |
| this.entityResolver = new PersistenceEntityResolver(); |
| this.classLoader = ConversionManager.getDefaultManager().getLoader(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Will return the the resource name with full path of the resource file. |
| */ |
| public String getResourcePath() { |
| return this.resourcePath; |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| public Vector<Throwable> getExceptionStore() { |
| return this.exceptionStore; |
| } |
| |
| /** |
| * PUBLIC: |
| * Returns the resource name we are trying to load. |
| */ |
| public String getResourceName() { |
| return this.resourceName; |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the resource name we are trying to load. |
| */ |
| public void setResourceName(String resourceName) { |
| this.resourceName = resourceName; |
| } |
| |
| /** |
| * PUBLIC: |
| * Returns the name of the Session in the sessions XML file desired to be loaded. |
| */ |
| public String getSessionName() { |
| return this.sessionName; |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the name of the Session in the sessions XML file desired to be loaded. |
| */ |
| public void setSessionName(String sessionName) { |
| this.sessionName = sessionName; |
| } |
| |
| /** |
| * PUBLIC: |
| * Return if the loaded session should be connected. |
| */ |
| public boolean shouldLogin() { |
| return shouldLogin; |
| } |
| |
| /** |
| * PUBLIC: |
| * Set if the loaded session should be connected. |
| */ |
| public void setShouldLogin(boolean shouldLogin) { |
| this.shouldLogin = shouldLogin; |
| } |
| |
| /** |
| * PUBLIC: |
| * Return if the loaded session should be refreshed from the file. |
| */ |
| public boolean shouldRefresh() { |
| return shouldRefresh; |
| } |
| |
| /** |
| * PUBLIC: |
| * Set if the loaded session should be refreshed from the file. |
| */ |
| public void setShouldRefresh(boolean shouldRefresh) { |
| this.shouldRefresh = shouldRefresh; |
| } |
| |
| /** |
| * PUBLIC: |
| * Return if the session will be refreshed from the file if the class loader requesting the load is different than the loaded session's class loader. |
| */ |
| public boolean shouldCheckClassLoader() { |
| return shouldCheckClassLoader; |
| } |
| |
| /** |
| * PUBLIC: |
| * Set if the session will be refreshed from the file if the class loader requesting the load is different than the loaded session's class loader. |
| */ |
| public void setShouldCheckClassLoader(boolean shouldCheckClassLoader) { |
| this.shouldCheckClassLoader = shouldCheckClassLoader; |
| } |
| |
| /** |
| * PUBLIC: |
| * Return the class loader that should be used to find the resource. |
| */ |
| public ClassLoader getClassLoader() { |
| return classLoader; |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the class loader that should be used to find the resource. |
| */ |
| public void setClassLoader(ClassLoader classLoader) { |
| this.classLoader = classLoader; |
| } |
| |
| /** |
| * INTERNAL: |
| * This method instantiates the parser and builds the document based on the |
| * schema. If the document is loaded without errors, then the configs are |
| * converted to sessions and stored on the session manager and true is |
| * returned to indicate success. |
| */ |
| public boolean load(SessionManager sessionManager, ClassLoader loader) { |
| Document document = loadDocument(loader); |
| |
| if(getExceptionStore().isEmpty()){ |
| if (document.getDocumentElement().getTagName().equals("sessions")) { |
| return buildSessionConfigs(sessionManager,loader,document,getProject()); |
| } |
| }else{ |
| //upon this time, we knew this could be either toplink sessions.xml or invalid eclipse session.xml. |
| if(document.getDocumentElement().getTagName().equals("toplink-sessions")){ |
| return buildSessionConfigs(sessionManager,loader,document,new XMLSessionConfigToplinkProject()); |
| }else{ |
| // Throw the exceptions we encountered |
| throw SessionLoaderException.finalException(getExceptionStore()); |
| } |
| } |
| // 9.0.4 session.xml, return false to indicate we should load with the XMLLoader |
| return false; |
| } |
| |
| private boolean buildSessionConfigs(SessionManager sessionManager, ClassLoader loader,Document document, Project project){ |
| // No errors occurred, unmasrshal the document which will return a |
| // SessionConfigs containing 0 or more SessionConfigs and send |
| // them through the factory to create actual Sessions |
| XMLContext context = new XMLContext(project); |
| XMLUnmarshaller unmarshaller = context.createUnmarshaller(); |
| SessionConfigs configs = (SessionConfigs)unmarshaller.unmarshal(document); |
| SessionsFactory factory = new SessionsFactory(); |
| Map<String, Session> sessions = factory.buildSessionConfigs(configs, loader); |
| for (Map.Entry<String, Session> entry: sessions.entrySet()) { |
| // Only add the session if missing. |
| if (!sessionManager.getSessions().containsKey(entry.getKey())) { |
| sessionManager.addSession(entry.getKey(), entry.getValue()); |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * INTERNAL: |
| * This method is to be used to load config objects for the Mapping Workbench |
| * only. Do not call this method. |
| */ |
| public SessionConfigs loadConfigsForMappingWorkbench(ClassLoader loader) { |
| return loadConfigsForMappingWorkbench(loader, true); |
| } |
| |
| /** |
| * INTERNAL: |
| * This method is to be used to load config objects for the Mapping Workbench |
| * only. Do not call this method. |
| */ |
| public SessionConfigs loadConfigsForMappingWorkbench(ClassLoader loader, boolean validate) { |
| Document document = loadDocument(loader, validate); |
| |
| if (getExceptionStore().isEmpty()) { |
| if (document.getDocumentElement().getTagName().equals("sessions")) { |
| // No errors occurred, unmarshal the document which will return a |
| // SessionConfigs containing 0 or more SessionConfigs |
| XMLContext context = new XMLContext(getProject()); |
| XMLUnmarshaller unmarshaller = context.createUnmarshaller(); |
| return (SessionConfigs)unmarshaller.unmarshal(document); |
| }else{ |
| // 9.0.4 session.xml or invalid xml format. |
| throw SessionLoaderException.InvalidSessionXML(); |
| } |
| } else { |
| if (document.getDocumentElement().getTagName().equals("toplink-sessions")) { |
| // No errors occurred, unmarshal the document which will return a |
| // SessionConfigs containing 0 or more SessionConfigs |
| XMLContext context = new XMLContext(new XMLSessionConfigToplinkProject()); |
| XMLUnmarshaller unmarshaller = context.createUnmarshaller(); |
| return (SessionConfigs)unmarshaller.unmarshal(document); |
| }else{ |
| // Throw the exceptions we encountered |
| throw SessionLoaderException.finalException(getExceptionStore()); |
| } |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Load a session.xml document. The error handler will capture all the |
| * errors and allow for a document to be returned. |
| */ |
| protected Document loadDocument(ClassLoader loader) { |
| return loadDocument(loader, SessionManager.shouldUseSchemaValidation()); |
| } |
| |
| /** |
| * INTERNAL: |
| * Load a session.xml document. The error handler will capture all the |
| * errors and allow for a document to be returned. |
| */ |
| protected Document loadDocument(ClassLoader loader, boolean validate) { |
| URL inURL = loader.getResource(this.resourceName); |
| // Also allow a file reference. |
| File inFile = new File(this.resourceName); |
| |
| if (inURL == null) { |
| // If loading the default resource name and it is not found, |
| // look in the META-INF directory. |
| if (this.resourceName.equals(DEFAULT_RESOURCE_NAME)) { |
| inURL = loader.getResource(DEFAULT_RESOURCE_NAME_IN_META_INF); |
| } |
| |
| if ((inURL == null) && (!inFile.exists())) { |
| throw ValidationException.noSessionsXMLFound(this.resourceName); |
| } |
| } |
| |
| if (inURL == null) { |
| this.resourcePath = inFile.getAbsolutePath(); |
| } else { |
| // Stored the loaded resource path, used in exception string if the |
| // resource is not found or if there are errors in the resource. |
| this.resourcePath = inURL.getPath(); |
| } |
| |
| XMLPlatform xmlPlatform = XMLPlatformFactory.getInstance().getXMLPlatform(); |
| XMLParser parser = xmlPlatform.newXMLParser(); |
| if (validate) { |
| parser.setValidationMode(XMLParser.SCHEMA_VALIDATION); |
| } else { |
| parser.setValidationMode(XMLParser.NONVALIDATING); |
| } |
| |
| parser.setWhitespacePreserving(false); |
| parser.setXMLSchema(loader.getResource(ECLIPSELINK_SESSIONS_SCHEMA)); |
| parser.setEntityResolver(this.entityResolver); |
| parser.setErrorHandler(new XMLSessionConfigLoaderErrorHandler()); |
| |
| if (inURL == null) { |
| return parser.parse(inFile); |
| } else { |
| return parser.parse(inURL); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * <p><b>Purpose</b>: Provide a mechanism to swallow all parsing errors |
| * |
| * @see ErrorHandler |
| * @since TopLink 10.1.3 |
| * @author Guy Pelletier |
| */ |
| public class XMLSessionConfigLoaderErrorHandler implements ErrorHandler { |
| |
| /** |
| * Default constructor. |
| */ |
| public XMLSessionConfigLoaderErrorHandler() { |
| } |
| |
| @Override |
| public void warning(SAXParseException e) throws SAXException { |
| getExceptionStore().add(SessionLoaderException.failedToParseXML(ExceptionLocalization.buildMessage("parsing_warning"), e.getLineNumber(), e.getColumnNumber(), e)); |
| } |
| |
| @Override |
| public void error(SAXParseException e) throws SAXException { |
| getExceptionStore().add(e); |
| } |
| |
| @Override |
| public void fatalError(SAXParseException e) throws SAXException { |
| getExceptionStore().add(e); |
| } |
| } |
| } |