/** * 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.remote.connections; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.util.List; import org.apache.http.client.HttpClient; import org.apache.jena.jdbc.JdbcCompatibility; import org.apache.jena.jdbc.connections.JenaConnection; import org.apache.jena.jdbc.remote.metadata.RemoteEndpointMetadata; import org.apache.jena.jdbc.remote.statements.RemoteEndpointPreparedStatement; import org.apache.jena.jdbc.remote.statements.RemoteEndpointStatement; import org.apache.jena.jdbc.statements.JenaPreparedStatement; import org.apache.jena.jdbc.statements.JenaStatement; /** * Represents a connection to a remote endpoint * */ public class RemoteEndpointConnection extends JenaConnection { private String queryService, updateService; private boolean closed = false; private boolean readonly = false; private List<String> defaultGraphUris; private List<String> namedGraphUris; private List<String> usingGraphUris; private List<String> usingNamedGraphUris; private HttpClient client; private DatabaseMetaData metadata; private String selectResultsType, modelResultsType; /** * Creates a new remote connection * * @param queryEndpoint * SPARQL Query Endpoint * @param updateEndpoint * SPARQL Update Endpoint * @param holdability * Result Set holdability * @param compatibilityLevel * JDBC compatibility level, see {@link JdbcCompatibility} * @throws SQLException * Thrown if there is a problem creating the connection */ public RemoteEndpointConnection(String queryEndpoint, String updateEndpoint, int holdability, int compatibilityLevel) throws SQLException { this(queryEndpoint, updateEndpoint, null, null, null, null, null, holdability, compatibilityLevel, null, null); } /** * Creates a new remote connection * * @param queryEndpoint * SPARQL Query Endpoint * @param updateEndpoint * SPARQL Update Endpoint * @param defaultGraphUris * Default Graph URIs for SPARQL queries * @param namedGraphUris * Named Graph URIs for SPARQL queries * @param usingGraphUris * Default Graph URIs for SPARQL updates * @param usingNamedGraphUris * Named Graph URIs for SPARQL updates * @param client * HTTP client * @param holdability * Result Set holdability * @param compatibilityLevel * JDBC compatibility level, see {@link JdbcCompatibility} * @param selectResultsType * Results Type to request for {@code SELECT} queries against the * remote endpoint * @param modelResultsType * Results Type to request for {@code CONSTRUCT} and * {@code DESCRIBE} queries against the remote endpoint * @throws SQLException * Thrown if there is a problem creating the connection */ public RemoteEndpointConnection(String queryEndpoint, String updateEndpoint, List<String> defaultGraphUris, List<String> namedGraphUris, List<String> usingGraphUris, List<String> usingNamedGraphUris, HttpClient client, int holdability, int compatibilityLevel, String selectResultsType, String modelResultsType) throws SQLException { super(holdability, true, Connection.TRANSACTION_NONE, compatibilityLevel); if (queryEndpoint == null && updateEndpoint == null) throw new SQLException("Must specify one/both of a query endpoint and update endpoint"); this.queryService = queryEndpoint; this.updateService = updateEndpoint; this.readonly = this.updateService == null; this.defaultGraphUris = defaultGraphUris; this.namedGraphUris = namedGraphUris; this.usingGraphUris = usingGraphUris; this.usingNamedGraphUris = usingNamedGraphUris; this.client = client; this.metadata = new RemoteEndpointMetadata(this); this.selectResultsType = selectResultsType; this.modelResultsType = modelResultsType; } /** * Gets the SPARQL query endpoint that is in use * * @return Endpoint URI or null for write only connections */ public String getQueryEndpoint() { return this.queryService; } /** * Gets the SPARQL update endpoint that is in use * * @return Endpoint URI or null for read-only connections */ public String getUpdateEndpoint() { return this.updateService; } /** * Gets the default graphs for SPARQL queries (may be null if none were set) * * @return Default graphs */ public List<String> getDefaultGraphURIs() { return this.defaultGraphUris; } /** * Gets the named graphs for SPARQL queries (may be null if none were set) * * @return Named graphs */ public List<String> getNamedGraphURIs() { return this.namedGraphUris; } /** * Gets the default graphs for SPARQL updates (may be null if none were set) * * @return Default graphs */ public List<String> getUsingGraphURIs() { return this.usingGraphUris; } /** * Gets the named graphs for SPARQL updates (may be null if none were set) * * @return Named graphs */ public List<String> getUsingNamedGraphURIs() { return this.usingNamedGraphUris; } /** * Gets the results type that will be requested from the remote endpoint for * {@code SELECT} queries * * @return Select results type if set, otherwise null which indicates that * the ARQ default will be used */ public String getSelectResultsType() { return this.selectResultsType; } /** * Gets the results type that will be requested from the remote endpoint for * {@code CONSTRUCT} and {@code DESCRIBE} queries. * * @return Model results type if set, otherwise null which indicates that * the ARQ default will be used */ public String getModelResultsType() { return this.modelResultsType; } @Override protected void closeInternal() { this.closed = true; } @Override protected JenaStatement createStatementInternal(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { if (this.isClosed()) throw new SQLException("Cannot create a statement after the connection was closed"); if (resultSetType == ResultSet.TYPE_SCROLL_SENSITIVE) throw new SQLFeatureNotSupportedException( "Remote endpoint backed connection do not support scroll sensitive result sets"); if (resultSetConcurrency != ResultSet.CONCUR_READ_ONLY) throw new SQLFeatureNotSupportedException("Remote endpoint backed connections only support read-only result sets"); return new RemoteEndpointStatement(this, this.client, resultSetType, ResultSet.FETCH_FORWARD, 0, resultSetHoldability); } @Override protected JenaPreparedStatement createPreparedStatementInternal(String sparql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { if (this.isClosed()) throw new SQLException("Cannot create a statement after the connection was closed"); if (resultSetType == ResultSet.TYPE_SCROLL_SENSITIVE) throw new SQLFeatureNotSupportedException( "Remote endpoint backed connection do not support scroll sensitive result sets"); if (resultSetConcurrency != ResultSet.CONCUR_READ_ONLY) throw new SQLFeatureNotSupportedException("Remote endpoint backed connections only support read-only result sets"); return new RemoteEndpointPreparedStatement(sparql, this, this.client, resultSetType, ResultSet.FETCH_FORWARD, 0, resultSetHoldability); } @Override public boolean isClosed() { return this.closed; } @Override public boolean isReadOnly() { return this.readonly; } @Override public boolean isValid(int timeout) { return !this.isClosed(); } @Override public void setReadOnly(boolean readOnly) throws SQLException { if (this.isClosed()) throw new SQLException("Cannot set read-only mode on a closed connection"); if (readOnly) { this.readonly = readOnly; } else { if (this.updateService == null) throw new SQLException("Cannot set connection to read/write as it was created without an update endpoint"); this.readonly = readOnly; } } @Override public DatabaseMetaData getMetaData() { return this.metadata; } @Override protected void commitInternal() { // No-op } @Override protected void rollbackInternal() { // No-op } @Override protected void checkTransactionIsolation(int level) throws SQLException { switch (level) { case Connection.TRANSACTION_NONE: return; default: throw new SQLFeatureNotSupportedException("Transactions are not supported for remote endpoint backed connections"); } } }