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

org.apache.cayenne.project.upgrade.BaseUpgradeHandler Maven / Gradle / Ivy

/*****************************************************************
 *   Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 ****************************************************************/
package org.apache.cayenne.project.upgrade;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;

import org.apache.cayenne.ConfigurationException;
import org.apache.cayenne.configuration.DataChannelDescriptor;
import org.apache.cayenne.map.DataMap;
import org.apache.cayenne.map.EntityResolver;
import org.apache.cayenne.resource.Resource;
import org.apache.cayenne.util.Util;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

/**
 * A common superclass of UpgradeHandlers.
 * 
 * @since 3.1
 */
// there's no guarantee this will survive the further version upgrades, but for
// now all
// the code here seems like version-agnostic
public abstract class BaseUpgradeHandler implements UpgradeHandler {

	static final String UNKNOWN_VERSION = "0";

	protected Resource projectSource;
	protected UpgradeMetaData metaData;

	public BaseUpgradeHandler(Resource projectSource) {

		if (projectSource == null) {
			throw new NullPointerException("Null project source");
		}

		this.projectSource = projectSource;
	}

	/**
	 * Creates a single common EntityResolver for all project DataMaps, setting
	 * it as a namespace for all of them. This is needed for resolving cross-map
	 * relationships.
	 */
	protected void attachToNamespace(DataChannelDescriptor channelDescriptor) {
		EntityResolver entityResolver = new EntityResolver(channelDescriptor.getDataMaps());

		for (DataMap map : entityResolver.getDataMaps()) {
			map.setNamespace(entityResolver);
		}
	}

	@Override
	public Resource getProjectSource() {
		return projectSource;
	}

	@Override
	public UpgradeMetaData getUpgradeMetaData() {
		// no attempts at thread-safety... shouldn't be needed for upgrades
		if (metaData == null) {
			metaData = loadMetaData();
		}

		return metaData;
	}

	@Override
	public Resource performUpgrade() throws ConfigurationException {
		UpgradeMetaData metaData = getUpgradeMetaData();
		switch (metaData.getUpgradeType()) {
		case DOWNGRADE_NEEDED:
			throw new ConfigurationException("Downgrade can not be performed");
		case INTERMEDIATE_UPGRADE_NEEDED:
			throw new ConfigurationException("Upgrade can not be performed - intermediate version upgrade needed");
		case UPGRADE_NEEDED:
			return doPerformUpgrade(metaData);
		default:
			return getProjectSource();
		}
	}

	/**
	 * Does the actual project upgrade, assuming the caller already verified
	 * that the upgrade is possible.
	 * 
	 * @param metaData
	 *            object describing the type of upgrade
	 */
	protected abstract Resource doPerformUpgrade(UpgradeMetaData metaData) throws ConfigurationException;

	/**
	 * Creates a metadata object describing the type of upgrade needed.
	 */
	protected abstract UpgradeMetaData loadMetaData();

	/**
	 * A default method for quick extraction of the project version from an XML
	 * file.
	 */
	protected String loadProjectVersion() {

		RootTagHandler rootHandler = new RootTagHandler();
		URL url = projectSource.getURL();

		try (InputStream in = url.openStream();) {

			XMLReader parser = Util.createXmlReader();

			parser.setContentHandler(rootHandler);
			parser.setErrorHandler(rootHandler);
			parser.parse(new InputSource(in));
		} catch (SAXException e) {
			// expected ... handler will terminate as soon as it finds a root
			// tag.
		} catch (Exception e) {
			throw new ConfigurationException("Error reading configuration from %s", e, url);
		}

		return rootHandler.projectVersion != null ? rootHandler.projectVersion : UNKNOWN_VERSION;
	}

	/**
	 * Compares two String versions.
	 */
	protected int compareVersions(String v1, String v2) {

		if (v1.equals(v2)) {
			return 0;
		}

		double v1Double = decodeVersion(v1);
		double v2Double = decodeVersion(v2);
		return v1Double < v2Double ? -1 : 1;
	}

	protected double decodeVersion(String version) {
		if (version == null || version.trim().length() == 0) {
			return 0;
		}

		// leave the first dot, and treat remaining as a fraction
		// remove all non digit chars
		StringBuilder buffer = new StringBuilder(version.length());
		boolean dotProcessed = false;
		for (int i = 0; i < version.length(); i++) {
			char nextChar = version.charAt(i);
			if (nextChar == '.' && !dotProcessed) {
				dotProcessed = true;
				buffer.append('.');
			} else if (Character.isDigit(nextChar)) {
				buffer.append(nextChar);
			}
		}

		return Double.parseDouble(buffer.toString());
	}

	class RootTagHandler extends DefaultHandler {

		private String projectVersion;

		@Override
		public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {

			this.projectVersion = attributes.getValue("", "project-version");

			// bail right away - we are not interested in reading this to the
			// end
			throw new SAXException("finished");
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy