/** * 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.jena.jdbc.tdb; import java.io.File; import java.sql.DriverManager; import java.sql.DriverPropertyInfo; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; import java.util.Properties; import org.apache.jena.jdbc.JenaDriver; import org.apache.jena.jdbc.connections.JenaConnection; import org.apache.jena.jdbc.tdb.connections.TDBConnection; import org.apache.jena.query.Dataset ; import org.apache.jena.system.JenaSystem ; import org.apache.jena.tdb.TDBFactory ; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * <p> * A Jena JDBC driver which creates connections to TDB datasets * </p> * <h3> * Connection URL</h3> * <p> * This driver expects a URL of the following form: * </p> * * <pre> * jdbc:jena:tdb:location=/path/to/dataset;must-exist=false * </pre> * <p> * The {@code location} parameter is used to refer to a folder containing the * TDB dataset you wish to load. The {@code must-exist} parameter indicates * whether the TDB dataset must already exist, if false then the driver will * create a new empty TDB dataset in that location if a TDB dataset does not * already exist. * </p> * <p> * Connections to TDB always support transactions and operate in auto-commit * mode by default. * </p> */ public class TDBDriver extends JenaDriver { private static final Logger LOGGER = LoggerFactory.getLogger(TDBDriver.class); /** * Constant for the TDB driver prefix, this is appended to the base * {@link JenaDriver#DRIVER_PREFIX} to form the URL prefix for JDBC * Connection URLs for this driver */ public static final String TDB_DRIVER_PREFIX = "tdb:"; /** * Constant for driver parameter that sets the location of the TDB dataset */ public static final String PARAM_LOCATION = "location"; /** * Constant for special value which may be used as the value of the * {@code location} parameter ({@link #PARAM_LOCATION}) to indicate that a * pure in-memory dataset is desired, this should not be used for anything * other than trivial testing */ public static final String LOCATION_MEM = "memory"; /** * Constant for driver parameter that sets whether the TDB dataset must * already exist */ public static final String PARAM_MUST_EXIST = "must-exist"; /** * Static initializer block which ensures the driver gets registered */ static { try { JenaSystem.init() ; register(); } catch (SQLException e) { throw new RuntimeException("Failed to register Jena TDB JDBC Driver", e); } } /** * Registers the driver with the JDBC {@link DriverManager} * * @throws SQLException * Thrown if the driver cannot be registered */ public static synchronized void register() throws SQLException { DriverManager.registerDriver(new TDBDriver()); } /** * Creates a new TDB driver */ public TDBDriver() { super(0, 1, TDB_DRIVER_PREFIX); } @Override protected JenaConnection connect(Properties props, int compatibilityLevel) throws SQLException { String location = props.getProperty(PARAM_LOCATION); if (location == null) throw new SQLException("Required connection parameter " + PARAM_LOCATION + " is not present in the connection URL or the provided Properties object"); // Determine location boolean useMem = this.isSetToValue(props, PARAM_LOCATION, LOCATION_MEM); File loc = new File(location); if (useMem) { LOGGER.warn("TDB Driver connection string specifies use of a pure in-memory dataset, this is not recommended for anything other than basic testing"); } else { if (!loc.isAbsolute()) { LOGGER.warn("TDB Driver connection string specifies location " + loc.getAbsolutePath() + ", if this was not the expected location consider using an absolute instead of a relative path"); } else { LOGGER.info("TDB Driver connection string specifies location " + loc.getAbsolutePath()); } } // Validate location if required if (this.isTrue(props, PARAM_MUST_EXIST) && !useMem) { if (!loc.exists()) { throw new SQLException("TDB Driver connection string specifies location " + loc.getAbsolutePath() + " which does not exist, correct the " + PARAM_LOCATION + " parameter or set the " + PARAM_MUST_EXIST + " parameter to false"); } else if (!loc.isDirectory()) { throw new SQLException("TDB Driver connection string specifies location " + loc.getAbsolutePath() + " which is not a directory, correct the " + PARAM_LOCATION + " parameter or set the " + PARAM_MUST_EXIST + " parameter to false"); } } // Open the TDB dataset try { Dataset tdb = useMem ? TDBFactory.createDataset() : TDBFactory.createDataset(location); // Return a new connection for the TDB dataset return new TDBConnection(tdb, ResultSet.HOLD_CURSORS_OVER_COMMIT, true, compatibilityLevel); } catch (SQLException e) { throw e; } catch (Exception e) { throw new SQLException("Unexpected error establishing TDB driver connection, see inner exception for details", e); } } @Override protected DriverPropertyInfo[] getPropertyInfo(Properties connProps, List<DriverPropertyInfo> baseDriverProps) { DriverPropertyInfo[] driverProps = new DriverPropertyInfo[2 + baseDriverProps.size()]; this.copyBaseProperties(driverProps, baseDriverProps, 2); // Location parameter driverProps[0] = new DriverPropertyInfo(PARAM_LOCATION, connProps.getProperty(PARAM_LOCATION)); driverProps[0].required = true; driverProps[0].description = "Sets the location of a TDB dataset, should be a file system path. The value " + LOCATION_MEM + " may be used for a non-persistent in-memory dataset but this should only be used for testing"; // Must Exist parameter driverProps[1] = new DriverPropertyInfo(PARAM_MUST_EXIST, connProps.getProperty(PARAM_MUST_EXIST)); driverProps[1].required = false; driverProps[1].choices = new String[] { "true", "false" }; driverProps[1].description = "If set to true requests that the driver check whether the " + PARAM_LOCATION + " parameter refers to an existing location before establishing a connection"; return driverProps; } }