org.mxupdate.eclipse.MXAdapter.java Source code

Java tutorial

Introduction

Here is the source code for org.mxupdate.eclipse.MXAdapter.java

Source

/*
 * Copyright 2008-2010 The MxUpdate Team
 *
 * Licensed 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.
 *
 * Revision:        $Rev$
 * Last Changed:    $Date$
 * Last Changed By: $Author$
 */

package org.mxupdate.eclipse;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectOutputStream;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import matrix.db.Context;
import matrix.db.JPO;
import matrix.db.MQLCommand;
import matrix.util.MatrixException;

import org.apache.commons.codec.binary.Base64;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.preference.IPreferenceStore;
import org.mxupdate.eclipse.console.Console;

/**
 * Adapter to the MX database.
 *
 * @author The MxUpdate Team
 * @version $Id$
 */
public class MXAdapter implements IDeploymentAdapter {
    /**
     * Key of the URL preference.
     */
    public static final String PREF_URL = "url"; //$NON-NLS-1$

    /**
     * Key of the name preference.
     */
    public static final String PREF_NAME = "name"; //$NON-NLS-1$

    /**
     * Key of the password preference.
     */
    public static final String PREF_PASSWORD = "password"; //$NON-NLS-1$

    /**
     * Key of the preference if password could be stored.
     */
    public static final String PREF_STORE_PASSWORD = "storePassword"; //$NON-NLS-1$

    /**
     * Key of the update by file content preference.
     */
    public static final String PREF_UPDATE_FILE_CONTENT = "updateByFileContent"; //$NON-NLS-1$

    /**
     * Key name of the properties stored in the preferences.
     */
    private static final String PREF_PROPERTIES = "pluginProperties"; //$NON-NLS-1$

    /**
     * Key if the properties are configured in external property file.
     */
    public static final String PREF_EXTERNAL_CONFIGURED = "externalConfigured"; //$NON-NLS-1$

    /**
     * Key for the external property file.
     */
    public static final String PREF_PROP_FILE = "propertyFile"; //$NON-NLS-1$

    /**
     * Key of the property key host (URL) preference.
     */
    public static final String PREF_PROP_KEY_URL = "propKeyUrl"; //$NON-NLS-1$

    /**
     * Key of the property key user name preference.
     */
    public static final String PREF_PROP_KEY_NAME = "propKeyName"; //$NON-NLS-1$

    /**
     * Key of the property key password preference.
     */
    public static final String PREF_PROP_KEY_PASSWORD = "propKeyPassword"; //$NON-NLS-1$

    /**
     * Name and place of the manifest file.
     *
     * @see #getPlugInVersion()
     */
    private static final String MANIFEST_FILE = "META-INF/MANIFEST.MF"; //$NON-NLS-1$

    /**
     * Label of the bundle version within the <code>META-INF/MANIFEST.MF</code>
     * file.
     *
     * @see #getPlugInVersion()
     */
    private static final String TEXT_BUNDLE_VERSION = "Bundle-Version:"; //$NON-NLS-1$

    /**
     * Length of the label of the bundle version within the
     * <code>META-INF/MANIFEST.MF</code> file.
     *
     * @see #getPlugInVersion()
     */
    private static final int LENGTH_BUNDLE_VERSION = MXAdapter.TEXT_BUNDLE_VERSION.length();

    /**
     * Holds the link to the preferences.
     */
    private final IPreferenceStore preferences;

    /**
     * MxUpdate plug-in console.
     *
     * @see #showConsole()
     */
    private final Console console;

    /**
     * Context to the MX database.
     *
     * @see #connected
     * @see #connect()
     * @see #disconnect()
     */
    private Context mxContext;

    /**
     * Is {@link #mxContext} connected to the MX database?
     *
     * @see #mxContext
     * @see #connect()
     * @see #disconnect()
     */
    private boolean connected = false;

    /**
     * Initializes the MX adapter.
     *
     * @param _preferences  preference store
     * @param _console      console used for logging purposes
     */
    MXAdapter(final IPreferenceStore _preferences, final Console _console) {
        this.preferences = _preferences;
        this.console = _console;
    }

    /**
     * Connects to the MX database.
     *
     * @return <i>true</i> if already connected or connect to MX database was
     *         successfully; otherwise <i>false</i> is returned
     * @see #connected
     * @see #mxContext
     * @see #checkVersions()
     */
    public boolean connect() {
        boolean connect = false;
        if (this.connected) {
            connect = true;
            this.console.logInfo(Messages.getString("MXAdapter.AlreadyConnected")); //$NON-NLS-1$
        } else {
            final boolean connectAllowed;
            final String user;
            final String passwd;
            final String host;

            // is external configured
            if (this.preferences.getBoolean(MXAdapter.PREF_EXTERNAL_CONFIGURED)) {
                final String propFile = this.preferences.getString(MXAdapter.PREF_PROP_FILE);
                final String propHost = this.preferences.getString(MXAdapter.PREF_PROP_KEY_URL);
                final String propName = this.preferences.getString(MXAdapter.PREF_PROP_KEY_NAME);
                final String propPass = this.preferences.getString(MXAdapter.PREF_PROP_KEY_PASSWORD);

                this.console.logInfo(Messages.getString("MXAdapter.ReadExternalFile", propFile)); //$NON-NLS-1$
                final Properties extProps = new Properties();
                boolean read = false;
                try {
                    extProps.load(new FileInputStream(propFile));
                    read = true;
                } catch (final FileNotFoundException e) {
                    this.console.logError(Messages.getString("MXAdapter.ExceptionExternalFileOpenFailed"), e); //$NON-NLS-1$
                } catch (final IOException e) {
                    this.console.logError(Messages.getString("MXAdapter.ExceptionExternalFileOpenFailed"), e); //$NON-NLS-1$
                }

                if (!read) {
                    connectAllowed = false;
                    host = null;
                    user = null;
                    passwd = null;
                } else if ((propName == null) || "".equals(propName) || (propPass == null) || "".equals(propPass)) {
                    host = extProps.getProperty(propHost);
                    final MXLoginPage loginPage = new MXLoginPage(host,
                            this.preferences.getString(MXAdapter.PREF_NAME));
                    loginPage.open();
                    connectAllowed = loginPage.isOkPressed();
                    user = loginPage.getUserName();
                    passwd = loginPage.getPassword();
                } else {
                    connectAllowed = true;
                    host = extProps.getProperty(propHost);
                    user = extProps.getProperty(propName);
                    passwd = extProps.getProperty(propPass);
                }

                // internal configured
            } else {
                host = this.preferences.getString(MXAdapter.PREF_URL);
                if (!this.preferences.getBoolean(MXAdapter.PREF_STORE_PASSWORD)) {
                    final MXLoginPage loginPage = new MXLoginPage(host,
                            this.preferences.getString(MXAdapter.PREF_NAME));
                    loginPage.open();
                    connectAllowed = loginPage.isOkPressed();
                    user = loginPage.getUserName();
                    passwd = loginPage.getPassword();
                } else {
                    connectAllowed = true;
                    user = this.preferences.getString(MXAdapter.PREF_NAME);
                    passwd = this.preferences.getString(MXAdapter.PREF_PASSWORD);
                }
            }

            if (connectAllowed) {

                try {
                    this.mxContext = new Context(host);
                    this.mxContext.resetContext(user, passwd, null);
                    this.mxContext.connect();
                    this.connected = this.mxContext.isConnected();
                    connect = true;
                    this.console.logInfo(Messages.getString("MXAdapter.ConnectedTo", host)); //$NON-NLS-1$

                    // read properties
                    final String newProps = this.execute("exec prog org.mxupdate.plugin.GetProperties"); //$NON-NLS-1$
                    final String curProps = this.preferences.getString(MXAdapter.PREF_PROPERTIES);
                    if (!newProps.equals(curProps)) {
                        this.preferences.setValue(MXAdapter.PREF_PROPERTIES, newProps);
                        this.console.logInfo(Messages.getString("MXAdapter.PluginPropertiesChanged")); //$NON-NLS-1$
                    }
                } catch (final MatrixException e) {
                    this.console.logError(Messages.getString("MXAdapter.ConnectFailed"), e); //$NON-NLS-1$
                }

                // check versions
                if (this.connected) {
                    this.checkVersions();
                }
            }
        }
        return connect;
    }

    /**
     * Checks that the version of the MxUpdate Eclipse Plug-In and the version
     * of the MxUpdate Update Deployment Tool have the same major and minor
     * number within their versions. If not equal the plug-in automatically
     * disconnects from MX.
     *
     * @see #disconnect()
     * @see #getPlugInVersion()
     * @see #connect()
     */
    protected void checkVersions() {
        String pluginVersion = null;
        try {
            pluginVersion = this.getPlugInVersion();
        } catch (final IOException e) {
            this.console.logError(Messages.getString("MXAdapter.ExceptionGetPlugInVersion"), e); //$NON-NLS-1$
        }

        String updateVersion = null;
        try {
            updateVersion = this.execute("exec prog org.mxupdate.plugin.GetVersion").replaceAll("-", ".");
        } catch (final MatrixException e) {
            this.console.logError(Messages.getString("MXAdapter.ExceptionGetUpdateVersion"), e); //$NON-NLS-1$
        }

        final String[] pluginVersions = (pluginVersion != null) ? pluginVersion.split("\\.") : null;
        final String[] updateVersions = (updateVersion != null) ? updateVersion.split("\\.") : null;
        if ((pluginVersion == null) || (pluginVersions.length < 2) || (updateVersion == null)
                || (updateVersions.length < 2) || !pluginVersions[0].equals(updateVersions[0])
                || !pluginVersions[1].equals(updateVersions[1])) {
            this.console.logError(Messages.getString("MXAdapter.CheckVersionsNoConnectAllowed", //$NON-NLS-1$
                    pluginVersion, updateVersion));
        }
    }

    /**
     * Returns for this plug-in the version stored within manifest file.
     *
     * @return found plug-in version within manifest file
     * @throws IOException if the manifest file could not found or not opened
     * @see #MANIFEST_FILE
     */
    protected String getPlugInVersion() throws IOException {
        final InputStream in = this.getClass().getClassLoader().getResourceAsStream(MXAdapter.MANIFEST_FILE);
        final byte[] buffer = new byte[in.available()];
        in.read(buffer);
        in.close();

        final String manifest = new String(buffer);
        String version = null;
        for (final String line : manifest.split("\n")) {
            if (line.startsWith(MXAdapter.TEXT_BUNDLE_VERSION)) {
                version = line.substring(MXAdapter.LENGTH_BUNDLE_VERSION).trim();
                break;
            }
        }

        return version;
    }

    /**
     * Disconnect from the MX database.
     *
     * @return <i>true</i> if already disconnected or disconnect from MX
     *         database was successfully; otherwise <i>false</i> is returned
     * @see #connected
     * @see #mxContext
     */
    public boolean disconnect() {
        boolean disconnect = false;
        if (!this.connected) {
            this.console.logInfo(Messages.getString("MXAdapter.AlreadyDisconnected")); //$NON-NLS-1$
            disconnect = true;
        } else {
            try {
                this.mxContext.disconnect();
                this.mxContext = null;
                this.connected = false;
                disconnect = true;
                this.console.logInfo(Messages.getString("MXAdapter.Disconnected")); //$NON-NLS-1$
            } catch (final MatrixException e) {
                this.console.logError(Messages.getString("MXAdapter.DisconnectFailed"), e); //$NON-NLS-1$
            }
        }
        return disconnect;
    }

    /**
     * Updates given MX update files in the MX database. If
     * {@link #PREF_UPDATE_FILE_CONTENT} is set, also the file content is
     * transfered within the update (e.g. if an update on another server is
     * done).
     *
     * @param _files    MxUpdate file which must be updated
     * @param _compile  if <i>true</i> all JPOs are compiled; if <i>false</i>
     *                  no JPOs are compiled, only an update is done
     * @see #execMql(CharSequence)
     */
    public void update(final List<IFile> _files, final boolean _compile) {
        // update by file content
        if (this.preferences.getBoolean(MXAdapter.PREF_UPDATE_FILE_CONTENT)) {
            final Map<String, String> files = new HashMap<String, String>();
            for (final IFile file : _files) {
                try {
                    final InputStream in = new FileInputStream(file.getLocation().toFile());
                    final byte[] bytes = new byte[in.available()];
                    in.read(bytes);
                    in.close();
                    files.put(file.getLocation().toString(), new String(bytes, file.getCharset()));
                } catch (final UnsupportedEncodingException e) {
                    this.console.logError(Messages.getString("MXAdapter.ExceptionConvertFileContent", //$NON-NLS-1$
                            file.getLocation().toString()), e);
                } catch (final CoreException e) {
                    this.console.logError(Messages.getString("MXAdapter.ExceptionFileCharSet", //$NON-NLS-1$
                            file.getLocation().toString()), e);
                } catch (final IOException e) {
                    this.console.logError(Messages.getString("MXAdapter.ExceptionReadFileContentFailed", //$NON-NLS-1$
                            file.getLocation().toString()), e);
                }
            }
            try {
                this.console
                        .logInfo(this.jpoInvoke("org.mxupdate.plugin.Update", "updateByContent", files, _compile));
            } catch (final Exception e) {
                this.console.logError(Messages.getString("MXAdapter.ExceptionUpdateFailed", //$NON-NLS-1$
                        files.keySet().toString()), e);
            }
            // update by file names
        } else {
            final Set<String> fileNames = new HashSet<String>();
            for (final IFile file : _files) {
                fileNames.add(file.getLocation().toString());
            }
            try {
                this.console
                        .logInfo(this.jpoInvoke("org.mxupdate.plugin.Update", "updateByName", fileNames, _compile));
            } catch (final Exception e) {
                this.console.logError(Messages.getString("MXAdapter.ExceptionUpdateFailed", //$NON-NLS-1$
                        fileNames.toString()), e);
            }
        }
    }

    /**
     * Extract the TCL update code for given <code>_file</code> from MX.
     *
     * @param _file     name of the update file for which the TCL update code
     *                  within MX must be extracted
     * @return configuration item update code for given <code>_file</code>
     * @throws MatrixException if update code could not be extracted
     */
    public String extractCode(final IFile _file) throws MatrixException {
        return this.execute("exec prog org.mxupdate.plugin.GetMxUpdateCode '" //$NON-NLS-1$
                + _file.toString() + "'"); //$NON-NLS-1$

    }

    /**
     * Executes given MQL command and returns the trimmed result of the MQL
     * execution. The MX context {@link #mxContext} is connected to the data
     * base if not already done.
     *
     * @param _command  MQL command to execute
     * @return trimmed result of the MQL execution
     * @throws MatrixException if MQL execution fails
     * @see #mxContext
     * @see #connect()
     */
    public String execute(final CharSequence _command) throws MatrixException {
        if (!this.connected) {
            this.connect();
        }

        final MQLCommand mql = new MQLCommand();
        mql.executeCommand(this.mxContext, _command.toString());
        if ((mql.getError() != null) && !"".equals(mql.getError())) { //$NON-NLS-1$
            throw new MatrixException(mql.getError() + "\nMQL command was:\n" + _command);
        }
        return mql.getResult().trim();
    }

    /**
     * Calls given <code>_method</code> in <code>_jpo</code>. The MX context
     * {@link #mxContext} is connected to the database if not already done.
     *
     * @param _jpo          name of JPO to call
     * @param _method       method of the called <code>_jpo</code>
     * @param _parameters   list of all parameters for the <code>_jpo</code>
     *                      which are automatically encoded encoded
     * @return returned value from the called <code>_jpo</code>
     * @throws IOException      if the parameter could not be encoded
     * @throws MatrixException  if the called <code>_jpo</code> throws an
     *                          exception
     * @see #mxContext
     * @see #connect()
     */
    protected String jpoInvoke(final String _jpo, final String _method, final Object... _parameters)
            throws IOException, MatrixException {
        if (!this.connected) {
            this.connect();
        }

        // encode parameters
        final String[] paramStrings = new String[_parameters.length];
        int idx = 0;
        for (final Object parameter : _parameters) {
            final ByteArrayOutputStream out = new ByteArrayOutputStream();
            final ObjectOutputStream oos = new ObjectOutputStream(out);
            oos.writeObject(parameter);
            oos.close();
            paramStrings[idx++] = new String(Base64.encodeBase64(out.toByteArray()));
        }

        return (String) JPO.invoke(this.mxContext, _jpo, null, _method, paramStrings, String.class);
    }
}