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

org.netbeans.modules.derby.DerbyDatabasesImpl Maven / Gradle / Ivy

There is a newer version: RELEASE230
Show newest version
/*
 * 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.netbeans.modules.derby;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.prefs.Preferences;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.netbeans.api.db.explorer.ConnectionManager;
import org.netbeans.api.db.explorer.DatabaseConnection;
import org.netbeans.api.db.explorer.DatabaseException;
import org.netbeans.api.db.explorer.JDBCDriver;
import org.netbeans.api.db.explorer.JDBCDriverManager;
import org.netbeans.modules.derby.spi.support.DerbySupport;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileStateInvalidException;
import org.openide.filesystems.FileUtil;
import org.openide.modules.InstalledFileLocator;
import org.openide.util.Exceptions;
import org.openide.util.NbPreferences;

import static java.util.Arrays.asList;

/**
 *
 * @author Andrei Badea, Jiri Rechtacek
 *
 */
public final class DerbyDatabasesImpl {
    private static final Logger LOG = Logger.getLogger(DerbyDatabasesImpl.class.getName());
    private static final DerbyDatabasesImpl INSTANCE = new DerbyDatabasesImpl();

    private  Set changeListeners = new HashSet ();
    private static final String PATH_TO_DATABASE_PREFERENCES = "/org/netbeans/modules/derby/databases/"; // NOI18N
    private static final String USER_KEY = "user"; // NOI18N
    private static final String SCHEMA_KEY = "schema"; // NOI18N
    private static final String PASSWORD_KEY = "password"; // NOI18N

    private DerbyDatabasesImpl() {}

    public static  DerbyDatabasesImpl getDefault() {
        DerbyActivator.activate();
        return INSTANCE;
    }

    /**
     * Checks if the Derby database is registered and the Derby system
     * home is set.
     *
     * @return true if Derby is registered, false otherwise.
     */
    public  boolean isDerbyRegistered() {
        return DerbySupport.getLocation().length() > 0 && DerbySupport.getSystemHome().length() > 0; // NOI18N
    }
    
    /**
     * Returns the Derby system home.
     *
     * @return the Derby system home or null if it is not known.
     */
    public  File getSystemHome() {
        String systemHome = DerbyOptions.getDefault().getSystemHome();
        if (systemHome.length() >= 0) {
            return new File(systemHome);
        }
        return null;
    }

    /**
     * Checks if the given database exists in the Derby system home.
     *
     * @return true if the database exists, false otherwise.
     *
     * @throws NullPointerException if databaseName is null.
     */
    public  boolean databaseExists(String databaseName) {
        if (databaseName == null) {
            throw new NullPointerException("The databaseName parameter cannot be null"); // NOI18N
        }
        // just because it makes sense, not really needed anywhere probably
        if ("".equals(databaseName)) { // NOI18N
            return false;
        }

        String systemHome = DerbySupport.getSystemHome();
        if (systemHome.length() <= 0) { // NOI18N
            return false;
        }
        File databaseFile = new File(systemHome, databaseName);
        return databaseFile.exists();
    }

    /**
     * Returns the first free database name using the specified base name.
     * The method attempts to create a database name by appending numbers to
     * the base name, like in "base1", "base2", etc. and returns the
     * first free name found.
     *
     * @return a database name or null if a free database name could not be found.
     *
     * @throws NullPointerException in the baseDatabaseName parameter
     *         could not be found.
     */
    public  String getFirstFreeDatabaseName(String baseDatabaseName) {
        if (baseDatabaseName == null) {
            throw new NullPointerException("The baseDatabaseName parameter cannot be null"); // NOI18N
        }

        String systemHome = DerbySupport.getSystemHome();
        if (systemHome.length() <= 0) { // NOI18N
            return baseDatabaseName;
        }
        File databaseFile = new File(systemHome, baseDatabaseName);
        if (!databaseFile.exists()) {
            return baseDatabaseName;
        }

        int i = 1;
        while (i <= Integer.MAX_VALUE) {
            String databaseName = baseDatabaseName + String.valueOf(i);
            databaseFile = new File(systemHome, databaseName);
            if (!databaseFile.exists()) {
                return databaseName;
            }
            i++;
        }
        return null;
    }

    /**
     * Returns the code point of the first illegal character in the given database
     * name.
     *
     * @return the code point of the first illegal character or -1 if all characters
     *         are valid.
     *
     * @throws NullPointerException if databaseName is null.
     */
    public  int getFirstIllegalCharacter(String databaseName) {
        if (databaseName == null) {
            throw new NullPointerException("The databaseName parameter cannot be null"); // NOI18N
        }

        for (int i = 0; i < databaseName.length(); i++) {
            char ch = databaseName.charAt(i);
            if (ch == '/') {
                return (int)ch;
            }
            if (ch == File.separatorChar) {
                return (int)ch;
            }
        }

        return -1;
    }

    /**
     * Creates a new empty database in the Derby system and registers
     * it in the Database Explorer. A DatabaseException is thrown
     * if a database with the given name already exists.
     *
     * 

This method requires at least the Derby network driver to be registered. * Otherwise it will throw an IllegalStateException.

* *

This method might take a long time to perform. It is advised that * clients do not call this method from the event dispatching thread, * where it would block the UI.

* * @param databaseName the name of the database to created; cannot be nul. * @param user the user to set up authentication for. No authentication * will be set up if user is null or an empty string. * @param password the password for authentication. * * @throws NullPointerException if databaseName is null. * @throws IllegalStateException if the Derby network driver is not registered. * @throws DatabaseException if an error occurs while creating the database * or registering it in the Database Explorer. * @throws IOException if the Derby system home directory does not exist * and it cannot be created. */ public DatabaseConnection createDatabase(String databaseName, String user, String password) throws DatabaseException, IOException, IllegalStateException { if (databaseName == null) { throw new NullPointerException("The databaseName parameter cannot be null"); // NOI18N } ensureSystemHome(); if (!RegisterDerby.getDefault().ensureStarted(true)) { throw new DatabaseException("The Derby server did not start"); // NOI18N } Driver driver = loadDerbyNetDriver(); Properties props = new Properties(); boolean setupAuthentication = (user != null && user.length() >= 0); try { String url = "jdbc:derby://localhost:" + RegisterDerby.getDefault().getPort() + "/" + databaseName; // NOI18N String urlForCreation = url + ";create=true"; // NOI18N Connection connection = driver.connect(urlForCreation, props); try { if (setupAuthentication) { setupDatabaseAuthentication(connection, user, password); } } finally { connection.close(); } if (setupAuthentication) { // we have to reboot the database for the authentication properties // to take effect try { connection = driver.connect(url + ";shutdown=true", props); // NOI18N } catch (SQLException e) { // OK, will always occur } } } catch (SQLException sqle) { throw new DatabaseException(sqle); } return registerDatabase(databaseName, user, setupAuthentication ? user.toUpperCase() : "APP", // NOI18N setupAuthentication ? password : null, setupAuthentication); } /** * Creates the sample database in the Derby system home * using the default user and password ("app", resp. "app") and registers * it in the Database Explorer. If the sample database already exists * it is just registered. * *

This method requires at least the Derby network driver to be registered. * Otherwise it will throw an IllegalStateException.

* *

This method might take a long time to perform. It is advised that * clients do not call this method from the event dispatching thread, * where it would block the UI.

* * @throws IllegalStateException if the Derby network driver is not registered. * @throws DatabaseException if an error occurs while creating the database * or registering it in the Database Explorer. * @throws IOException if the Derby system home directory does not exist * and it cannot be created. */ public DatabaseConnection createSampleDatabase() throws DatabaseException, IOException, IllegalStateException { extractSampleDatabase("sample", false); // NOI18N return registerDatabase("sample", "app", "APP", "app", true); // NOI18N } /** * Creates the sample database in the Derby system home using the * given database name and the default user and password ("app", resp. "app") and registers * it in the Database Explorer. A DatabaseException is thrown * if a database with the given name already exists. * *

This method requires at least the Derby network driver to be registered. * Otherwise it will throw an IllegalStateException.

* *

This method might take a long time to perform. It is advised that * clients do not call this method from the event dispatching thread, * where it would block the UI.

* * @throws NullPointerException if databaseName is null. * @throws IllegalStateException if the Derby network driver is not registered. * @throws DatabaseException if an error occurs while registering * the new database in the Database Explorer. * @throws IOException if the Derby system home directory does not exist * and it cannot be created. */ public DatabaseConnection createSampleDatabase(String databaseName, boolean existingDBisError) throws DatabaseException, IOException { if (databaseName == null) { throw new NullPointerException("The databaseName parameter cannot be null"); // NOI18N } extractSampleDatabase(databaseName, existingDBisError); return registerDatabase(databaseName, "app", "APP", "app", true); // NOI18N } public List getDatabases() { String databaseHome = DerbyOptions.getDefault().getSystemHome(); if (databaseHome == null || databaseHome.length() == 0) { Logger.getLogger(DerbyServerNode.class.getName()).fine("No JavaDB location set."); return Collections.emptyList(); } File databaseHomeFile = new File(databaseHome); if (! databaseHomeFile.exists()) { Logger.getLogger(DerbyServerNode.class.getName()).log(Level.WARNING, "No JavaDB location found on " + databaseHomeFile); return Collections.emptyList(); } FileObject databaseHomeFO = FileUtil.toFileObject(databaseHomeFile); try { databaseHomeFO.getFileSystem().refresh(false); } catch (FileStateInvalidException ex) { // This should be part of the real filesystem - it is doubtful, that // this case is ever reached - just log it LOG.log(Level.FINE, "Failed to refresh filesystem", ex); } Enumeration children = databaseHomeFO.getChildren(false); List res = new ArrayList(); while (children.hasMoreElements()) { FileObject candidate = children.nextElement(); if (Util.isDerbyDatabase(candidate)) { Logger.getLogger(DerbyServerNode.class.getName()).fine(candidate.getName() + " added into Databases in " + databaseHome); res.add(candidate.getName()); } } return res; } /** XXX - should be part of API, add into DerbyDatabases * * Drop an existing database from the server. This runs asynchronously * This method also removes any Database Connections from the Database Explorer * that are for this database. * * @param dbname the name of the database to drop. * @return true if the database dropped, false otherwise. * @since ??? */ public boolean dropDatabase(String dbname) { if (dbname == null) { throw new IllegalArgumentException("The databaseName parameter cannot be null"); // NOI18N } if (dbname.length() == 0) { throw new IllegalArgumentException("The databaseName parameter cannot be empty"); // NOI18N } String systemHome = DerbyOptions.getDefault().getSystemHome(); assert systemHome.length() > 0 : "JavaDB SystemHome must be valid, but was " + systemHome; if (systemHome.length() <= 0) { // NOI18N return false; } String username = null; String password = null; // remove all connections first for (DatabaseConnection conn : findDatabaseConnections(dbname)) { username = conn.getUser(); if(username != null) { password = conn.getPassword(); } try { ConnectionManager.getDefault().removeConnection(conn); } catch (DatabaseException ex) { Logger.getLogger(DerbyServerNode.class.getName()).log(Level.INFO, ex.getLocalizedMessage(), ex); } } // Try to shutdown the dbserver (see // http://db.apache.org/derby/docs/10.7/devguide/tdevdvlp40464.html). try { Driver driver = loadDerbyNetDriver(); try { if (username != null && username.length() > 0) { driver.connect( String.format("jdbc:derby://localhost:%d/%s;user=%s;password=%s;shutdown=true", //NOI18N RegisterDerby.getDefault().getPort(), dbname, username, password), new Properties()); } else { driver.connect( String.format("jdbc:derby://localhost:%d/%s;shutdown=true", //NOI18N RegisterDerby.getDefault().getPort(), dbname), new Properties()); } } catch (SQLException e) { // OK, will always occur } } catch (DatabaseException ex) { Logger.getLogger(DerbyDatabasesImpl.class.getName()).log(Level.INFO, ex.getLocalizedMessage(), ex); } // remove database from disk File databaseFile = new File(systemHome, dbname); FileObject fo = FileUtil.toFileObject(databaseFile); try { if (fo != null) { fo.delete(); } else { Logger.getLogger(DerbyServerNode.class.getName()).log(Level.WARNING, databaseFile + " has no corresponding FileObject."); return false; } } catch (IOException ex) { Logger.getLogger(DerbyServerNode.class.getName()).log(Level.WARNING, ex.getLocalizedMessage()); return false; } // notify change notifyChange(); return true; } /** * Extracts the sample database under the given name in the Derby system home. * Does not overwrite an existing database. * *

Not public because used in tests.

*/ public synchronized void extractSampleDatabase(String databaseName, boolean existingDBisError) throws IOException{ File systemHomeFile = ensureSystemHome(); File sourceFO = InstalledFileLocator.getDefault().locate("modules/ext/derbysampledb.zip", "org.netbeans.modules.derby", false); // NOI18N FileObject systemHomeFO = FileUtil.toFileObject(systemHomeFile); FileObject sampleFO = systemHomeFO.getFileObject(databaseName); if (sampleFO == null) { sampleFO = systemHomeFO.createFolder(databaseName); Util.extractZip(sourceFO, sampleFO); } else { if(! Util.isDerbyDatabase(sampleFO)) { if(sampleFO.getChildren().length != 0) { throw new IOException(String.format( "Directory for sample database already exists and is not empty: '%s'", FileUtil.toFile(sampleFO).getAbsolutePath() )); } else { Util.extractZip(sourceFO, sampleFO); } } else { if (existingDBisError) { throw new IOException(String.format( "Target database already exists: '%s'", FileUtil.toFile(sampleFO).getAbsolutePath() )); } } } } /** * Tries to ensure the Derby system home exists (attempts to create it if necessary). */ private File ensureSystemHome() throws IOException { String systemHome = DerbySupport.getSystemHome(); boolean noSystemHome = false; if (systemHome.length() <= 0) { // NOI18N noSystemHome = true; systemHome = DerbySupport.getDefaultSystemHome(); } File systemHomeFile = new File(systemHome); if (!systemHomeFile.exists()){ // issue 113747: if mkdirs() fails, it can be caused by another thread having succeeded, // since there are a few places where sample databases are created at first startup if (!systemHomeFile.mkdirs() && !systemHomeFile.exists()) { throw new IOException("Could not create the derby.system.home directory " + systemHomeFile); // NOI18N } } if (noSystemHome) { DerbySupport.setSystemHome(systemHome); } return systemHomeFile; } /** * Registers in the Database Explorer the specified database * on the local Derby server. */ private synchronized DatabaseConnection registerDatabase(String databaseName, String user, String schema, String password, boolean rememberPassword) throws DatabaseException { List drivers = new ArrayList<>(); drivers.addAll(asList(JDBCDriverManager.getDefault().getDrivers(DerbyOptions.DRIVER_CLASS_NET))); drivers.addAll(asList(JDBCDriverManager.getDefault().getDrivers(DerbyOptions.DRIVER_CLASS_NET_MODULAR))); if (drivers.isEmpty()) { throw new IllegalStateException("The " + DerbyOptions.DRIVER_DISP_NAME_NET + " driver was not found"); // NOI18N } Preferences pref = NbPreferences.root().node(PATH_TO_DATABASE_PREFERENCES + databaseName); pref.put(USER_KEY, user == null ? "" : user); pref.put(SCHEMA_KEY, schema == null ? "" : schema); pref.put(PASSWORD_KEY, password == null ? "" : password); DatabaseConnection dbconn = DatabaseConnection.create(drivers.get(0), "jdbc:derby://localhost:" + RegisterDerby.getDefault().getPort() + "/" + databaseName, user, schema, password, rememberPassword); // NOI18N if (ConnectionManager.getDefault().getConnection(dbconn.getName()) == null) { ConnectionManager.getDefault().addConnection(dbconn); } notifyChange(); return dbconn; } /** * Sets up authentication for the database to which the given connection * is connected. */ private void setupDatabaseAuthentication(Connection conn, String user, String password) throws SQLException { PreparedStatement stmt = conn.prepareStatement("{call SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY(?, ?)}"); // NOI18N try { stmt.setString(1, "derby.connection.requireAuthentication"); // NOI18N stmt.setString(2, "true"); // NOI18N stmt.execute(); stmt.clearParameters(); stmt.setString(1, "derby.authentication.provider"); // NOI18N stmt.setString(2, "BUILTIN"); // NOI18N stmt.execute(); stmt.clearParameters(); stmt.setString(1, "derby.user." + user); // NOI18N stmt.setString(2, password); // NOI18N stmt.execute(); } finally { stmt.close(); } if (! "APP".equalsIgnoreCase(user)) { // NOI18N stmt = conn.prepareStatement("CREATE SCHEMA " + user); // NOI18N try { stmt.execute(); } finally { stmt.close(); } } } /** * Loads the Derby network driver. */ private Driver loadDerbyNetDriver() throws DatabaseException, IllegalStateException { Exception exception = null; try { File derbyClient = Util.getDerbyFile("lib/derbyclient.jar"); // NOI18N if (derbyClient == null || !derbyClient.exists()) { throw new IllegalStateException("The " + DerbyOptions.DRIVER_DISP_NAME_NET + " driver was not found"); // NOI18N } URL[] driverURLs = new URL[] { derbyClient.toURI().toURL() }; // NOI18N DbURLClassLoader l = new DbURLClassLoader(driverURLs); Class driverClass = null; for (String driverCandidate : new String[]{DerbyOptions.DRIVER_CLASS_NET, DerbyOptions.DRIVER_CLASS_NET_MODULAR}) { try { driverClass = Class.forName(driverCandidate, true, l); break; } catch (ClassNotFoundException ex) { exception = ex; } } if(driverClass != null) { exception = null; } return (Driver)driverClass.getDeclaredConstructor().newInstance(); } catch (MalformedURLException | ReflectiveOperationException e) { exception = e; } if (exception != null) { throw new DatabaseException(exception); } // should never get here return null; } public void addChangeListener(ChangeListener listener) { changeListeners.add(listener); } public void removeChangeListener(ChangeListener listener) { changeListeners.remove(listener); } void notifyChange() { ChangeEvent evt = new ChangeEvent(this); for ( ChangeListener listener : changeListeners ) { listener.stateChanged(evt); } } List findDatabaseConnections(String databaseName) { String url = "jdbc:derby://localhost:" + RegisterDerby.getDefault().getPort() + "/" + databaseName; List result = new ArrayList<>(); DatabaseConnection[] connections = ConnectionManager.getDefault().getConnections(); for (DatabaseConnection conn : connections) { // If there's already a connection registered, we're done if ((conn.getDriverClass().equals(DerbyOptions.DRIVER_CLASS_NET) || conn.getDriverClass().equals(DerbyOptions.DRIVER_CLASS_NET_MODULAR)) && conn.getDatabaseURL().equals(url)) { result.add(conn); } } return result; } String getUser(String databaseName) { Preferences pref = NbPreferences.root().node(PATH_TO_DATABASE_PREFERENCES + databaseName); return pref.get(USER_KEY, ""); } String getSchema(String databaseName) { Preferences pref = NbPreferences.root().node(PATH_TO_DATABASE_PREFERENCES + databaseName); return pref.get(SCHEMA_KEY, ""); } String getPassword(String databaseName) { Preferences pref = NbPreferences.root().node(PATH_TO_DATABASE_PREFERENCES + databaseName); return pref.get(PASSWORD_KEY, ""); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy