blob: b6b949374d0f321b8932525fedfec097b682d7b8 [file] [log] [blame]
/*
* Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2018 IBM Corporation. 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:
// 05/05/2011-2.3 Chris Delahunt
// - 344837: Extensibility - Metadata Repository
// 08/29/2016 Jody Grassel
// - 500441: Eclipselink core has System.getProperty() calls that are not potentially executed under doPriv()
package org.eclipse.persistence.jpa.metadata;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.eclipse.persistence.internal.jpa.metadata.xml.XMLEntityMappingsReader;
import org.eclipse.persistence.internal.jpa.metadata.xml.XMLEntityMappings;
import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.eclipse.persistence.exceptions.ValidationException;
import org.eclipse.persistence.logging.SessionLog;
/**
* <p><b>Purpose</b>: Support reading metadata for a persistence unit in an XML format from a URL and if the property is undefined,
* it will look for a file.
*/
public class XMLMetadataSource extends MetadataSourceAdapter {
/**
* Default constructor.
*/
public XMLMetadataSource() {
}
/**
* This method returns a Reader for an EclipseLink-ORM.xml. It will use the
* PersistenceUnitProperties.METADATA_SOURCE_XML_URL property if available to create an
* InputStreamReader from a URL, and if not available, use the
* PersistenceUnitProperties.METADATA_SOURCE_XML_FILE property will be used to get a file
* resource from the classloader.
* It will throw a ValidationException if no reader can be returned.
*
* @param log - SessionLog used for status messages.
* @return Reader - a InputStreamReader with data in the form of an EclipseLink-orm.xml
*
*
* @see #getEntityMappings
*/
public Reader getEntityMappingsReader(Map<String, Object> properties, ClassLoader classLoader, SessionLog log) {
InputStreamReader reader = null;
//read from a URL
String mappingURLName = (String)getConfigPropertyLogDebug(
PersistenceUnitProperties.METADATA_SOURCE_XML_URL,
properties, log);
if (mappingURLName !=null && mappingURLName.length()!=0) {
try {
URL url = new URL(mappingURLName);
reader = new InputStreamReader(url.openStream());
} catch (IOException exception) {
throw ValidationException.fileError(exception);
}
}
//read a file using the classloader
if (reader == null) {
String mappingFileName = (String)getConfigPropertyLogDebug(
PersistenceUnitProperties.METADATA_SOURCE_XML_FILE,
properties, log);
if (mappingFileName != null && mappingFileName.length() > 0) {
try {
URL fileURL = getFileURL(mappingFileName, classLoader, log);
if (fileURL != null) {
reader = new InputStreamReader(fileURL.openStream());
}
} catch (IOException exception) {
throw ValidationException.fileError(exception);
}
}
}
if (reader == null) {
//being configured to use XMLMetadataSource and not having a source to read from should be an exception
throw ValidationException.missingXMLMetadataRepositoryConfig();
}
return reader;
}
/**
* This method is responsible for returning the object representation of the MetadataSource.
* This implementation makes a call to getEntityMappingsReader to get a Reader which is passed to an
* XMLUnmarshaller, and closes the reader in a finally block.
*
* @return XMLEntityMappings - object representation of the EclipseLink-orm.xml for this repository
*/
@Override
public XMLEntityMappings getEntityMappings(Map<String, Object> properties, ClassLoader classLoader, SessionLog log) {
Reader reader = getEntityMappingsReader(properties, classLoader, log);
if (reader == null) {
return null;
}
try {
return XMLEntityMappingsReader.read(getRepositoryName(), reader, classLoader, properties);
} finally {
if (reader!=null) {
try {
reader.close();
} catch (Exception e) {
//ignore so we rethrow original exception if there was one.
}
}
}
}
/**
* Used by getEntityMappings when creating the XMLEntityMappings as a way of describing where it was read from.
* Currently returns the current class's simple name.
*
* @return String - repository name to store in the XMLEntityMappings returned from getEntityMappings
*/
public String getRepositoryName() {
return getClass().getSimpleName();
}
/**
* PUBLIC: This method is responsible for returning additional persistence
* unit property overrides. It is called on initial deployment of the
* persistence unit and when the persistence unit is reloaded to allow
* customization of the persistence unit above and beyond what is packaged
* in the persistence.xml and what is code into the application.
* <p>
* <b>IMPORTANT</b>: Although any property can be changed using this
* approach it is important that users of this feature ensure compatible
* configurations are supplied. As an example; overriding an application to
* use RESOURCE_LOCAL when it was coded to use JTA would result in changes
* not be written to the database.
*
* PersistenceUnitProperties.METADATA_SOURCE_PROPERTIES_FILE property will be used to get a file
* resource from the classloader. Properties are read from the file.
* If the property either not specified or contains an empty string then returns null.
*
* @since EclipseLink 2.4
*/
@Override
public Map<String, Object> getPropertyOverrides(Map<String, Object> properties, ClassLoader classLoader, SessionLog log) {
String propertiesFileName = (String)getConfigPropertyLogDebug(
PersistenceUnitProperties.METADATA_SOURCE_PROPERTIES_FILE,
properties, log);
if (propertiesFileName == null || propertiesFileName.length() == 0) {
return null;
}
try {
URL fileURL = getFileURL(propertiesFileName, classLoader, log);
if (fileURL != null) {
Properties propertiesFromFile = new Properties();
propertiesFromFile.load(fileURL.openStream());
if (!propertiesFromFile.isEmpty()) {
return new HashMap(propertiesFromFile);
} else {
return null;
}
} else {
throw ValidationException.missingPropertiesFileForMetadataRepositoryConfig(propertiesFileName);
}
} catch (IOException exception) {
throw ValidationException.fileError(exception);
}
}
protected static URL getFileURL(String fileName, ClassLoader classLoader, SessionLog log) throws IOException {
Enumeration<URL> fileURLs = classLoader.getResources(fileName);
if (!fileURLs.hasMoreElements()){
fileURLs = classLoader.getResources("/./" + fileName);
}
if (fileURLs.hasMoreElements()) {
URL nextURL = fileURLs.nextElement();
if (fileURLs.hasMoreElements()) {
// Switched to warning, same file can be on the classpath twice in some deployments,
// should not be an error.
log.logThrowable(SessionLog.FINER, SessionLog.METADATA, ValidationException.nonUniqueRepositoryFileName(fileName));
}
return nextURL;
} else {
return null;
}
}
/**
* Check the provided map for an object with the given name. If that object is not available, check the
* System properties. Log the value returned if logging is enabled
* @param propertyName property name
* @param properties properties
* @param log logger
* @return object for the given name, null if not found
*/
public Object getConfigPropertyLogDebug(final String propertyName, Map<String, ?> properties, SessionLog log) {
return PropertyHelper.getConfigPropertyLogDebug(propertyName, properties, log);
}
}