/** * 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.sqoop.common.test.db; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.apache.derby.drda.NetworkServerControl; import org.apache.sqoop.common.test.db.types.DatabaseTypeList; import org.apache.sqoop.common.test.db.types.DerbyTypeList; import org.apache.sqoop.common.test.utils.LoggerWriter; import org.apache.sqoop.common.test.utils.NetworkUtils; import java.net.InetAddress; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; /** * Implementation of database provider that is based on embedded derby server. * * This provider will work out of the box without any extra configuration. */ public class DerbyProvider extends DatabaseProvider { private static final Logger LOG = Logger.getLogger(DerbyProvider.class); public static final String DRIVER = "org.apache.derby.jdbc.ClientDriver"; // Used port for this instance int port; NetworkServerControl server = null; // We've observed several cases where Derby did not start properly // from various reasons without any Exception being raised and any // subsequent call to server.ping() or server.stop() got the process // into zombie state waiting forever. Hence we're having boolean // variable that is guarding potentially dangerous calls. boolean started = false; @Override public void start() { // Start embedded server try { port = NetworkUtils.findAvailablePort(); LOG.info("Will bind to port " + port); server = new NetworkServerControl(InetAddress.getByName("localhost"), port); server.start(new LoggerWriter(LOG, Level.INFO)); // Start won't thrown an exception in case that it fails to start, one // have to explicitly call ping() in order to verify if the server is // up. Check DERBY-1465 for more details. // // In addition we've observed that in some scenarios ping() can get into // deadlock waiting on remote server forever and hence we're having // our own timeout handling around it. ExecutorService executorService = Executors.newSingleThreadExecutor(); Future future = executorService.submit(new Callable<Object>() { @Override public Object call() throws Exception { while (true) { try { server.ping(); break; } catch (Exception e) { LOG.warn("Could not ping derby server on port " + port, e); } Thread.sleep(1000); } return null; } }); future.get(10, TimeUnit.SECONDS); // Server successfully started at this point started = true; } catch (Exception e) { String message = "Can't start embedded Derby server"; LOG.fatal(message, e); throw new RuntimeException(message, e); } super.start(); } @Override public void stop() { super.stop(); // Shutdown embedded server try { if(started) { server.shutdown(); } } catch (Exception e) { String message = "Can't shut down embedded Derby server"; LOG.fatal(message, e); throw new RuntimeException(message, e); } } @Override public String escapeColumnName(String columnName) { return escape(columnName); } @Override public String escapeTableName(String tableName) { return escape(tableName); } @Override public String escapeSchemaName(String schemaName) { return escape(schemaName); } @Override public String escapeValueString(String value) { return "'" + value + "'"; } @Override public boolean isSupportingScheme() { return true; } public String escape(String entity) { return "\"" + entity + "\""; } @Override public String getJdbcDriver() { return DRIVER; } @Override public String getConnectionUrl() { return "jdbc:derby://localhost:" + port + "/memory:sqoop;create=true"; } @Override public String getConnectionUsername() { return null; } @Override public String getConnectionPassword() { return null; } @Override public DatabaseTypeList getDatabaseTypes() { return new DerbyTypeList(); } }